那些陌生又熟悉的前端面試題
過完年需要跳槽的小伙伴還是挺多的,又要開始刷前端面試題了!會不會有一種錯覺,看著這道面試題很熟,但是不知道該如何做?或者有答案又不知道是否正確?或者使用編輯器可以運行出來正確的答案,但是不知道怎么得來的,這些你都中招了嗎?
1、嚴格模式與非嚴格模式的區別,你了解多少?
JavaScript 語言是一門弱類型語言,存在許多類型錯誤,因此 ES6 引入了嚴格模式概念。
如果不加 ‘use strict’ 常規模式下就是屬于非嚴格模式。
嚴格模式
在 js 文件頂部添加 ‘use strict’ 就屬于嚴格模式,嚴格模式也可以指定在函數內部。
<script>
'use strict'
//或者函數內部
(function(){
'use strict'
})()
</script>
嚴格模式,是為 js 定義來了一種不同的解析與執行模型,在嚴格模式下,ECMAScipt 3 中一些不解和不確定的行為將得到處理,而且會對不安全的操作會拋出異常。‘use strict’ 會告訴瀏覽器引擎可以切換到嚴格模式執行。
嚴格模式與非嚴格模式區別
2、深淺拷貝的區別有哪些?
常見筆試題:
var person = {
name:"前端人",
hobby:['學習','敲代碼','潛水']
}
function copy(source){
var newObj = new Object()
for(var i in source){
if(source.hasOwnProperty(i)){
newObj[i] = source[i]
}
}
return newObj
}
var p1 = copy(person);
p1.name = "Web Person"
console.log(person.name)
console.log(p1.name)
p1.hobby = ["內卷"]
console.info(person.hobby)
console.info(p1.hobby)
/*運行結果:
前端人
Web Person
["學習", "敲代碼", "潛水"]
["內卷"]
*/
試試這道筆試題你會做嗎?
要說 js 的深淺拷貝,就不得不提 js 的兩大數據類型:基本數據類型和引用類型。
基本數據類型的變量名和值都存儲在棧中,對于引用類型的變量名存儲在棧中,而值存儲在堆中。由于存儲方式不同,所以導致了他們復制的時候方式不同。
賦值
基本數據類型賦值的時候,創建的基本數據類型會在內存中開辟一個新空間把值復制過來,而引用類型采用的是地址存儲,如果直接把一個引用數據直接賦值給另外一個數據,就相當于直接把自己存儲值的地址給了另外一個變量,所以改變一個的值,也會改變另外一個的值。
var a = 1;
var b = a;
b=2;
console.log(a) //1
console.log(b)//2
var p1 = {name:"前端人"}
var p2 = p1
p2.name = "打工仔"
console.log(p1.name) // '打工仔'
console.log(p2.name) // '打工仔'
深淺拷貝是如何定義的?
假設有 p 和 copyP 兩個變量,如果copyP 是拷貝了 p 的,我們通過修改 copyP 來觀察 p 是否發生改變,如果跟著改變,就是淺拷貝,如果是不改變,就說明是深拷貝。
基本類型復制的時候會開辟新的內存空間,所以兩個值是相互獨立的,引用類型復制的時候就要看是復制的內存地址還是復制一個新的堆。所以深拷貝主要針對的是引用類型的數據。
淺拷貝的常見的方式:
1、直接賦值
var arr1 = [1,2,3,4];
var arr2 = arr1;
2、Object.assign
var obj = {
a:1,
b:2
}
var o = Object.assign(obj)
深拷貝的常見方式:
引用數據類型最常用的就是 Object 和 Array ,引用數據內部的數據也可以是多樣化的,進行深拷貝時,也要適當地根據數據結構進行合適的復制方式,具體的深拷貝方法分別有:
1、數組中只包含基本數據類型
- 循環數組,將數組的值復制出來放入另一個新數組中
- 利用 slice 方法
- 借助 concat 方法
- 利用 from 方法
- 使用擴展符 ...
2、對象中只包含基本數據類型
- 利用 for in 循環,將對象的值拿出來
- 使用 Object 復制給一個新的空對象
- 使用 ... 擴展運算符
- 手動復制,將屬性值一個一個單獨復制
3、對象或數組里含有一層或多層引用數據類型時
- 使用 jQuery 的 extend 方法
- JSON.parse(JSON.stringify())
- 使用遞歸自己寫一個深拷貝的方法
深淺拷貝的常見應用主要是數據的增刪改操作。
3、this 的指向
大廠筆試題:
var name = 'window name'
var p1 = {
name:'p1 name',
showName:function(){
console.info(this.name)
}
}
var fn = p1.showName
fn()
p1.showName()
var p2 = {
name:'p2 name',
showName:function(fun){
fun()
}
}
p2.showName(p1.showName)
p2.showName = p1.showName
p2.showName()
/*
運行結果:
window name
p1 name
window name
p2 name
*/
這是一道關于 this 指向的面試題,接下來我們就說說 this 是如何指向的?
this 對象是運行時基于函數的執行環境綁定的:
- 在全局函數中,this 等于 window 。
- 函數上下文調用,嚴格模式下 this 為 undefined ,非嚴格模式下,this 指向 window 。
- 當函數被作為某個對象的方法被調用時,this 等于那個對象。如果使用 call apply 改變當前 this 時,將會指向為傳遞過來的那個 this 。
- 匿名函數的執行環境具有全局性,因此 this 指向 window。
- 構造函數內的 this 指向創建的實例對象。
- dom 事件處理函數,this 指向觸發該事件的元素。
- setTimeout 和 setInterval 中的 this 指向全局變量 window
看完上述 this 指向解釋,你就可以做上邊的那道面試題了。
如何改變 this 的指向?
call 、bind 和 apply 這三個函數都是用來改變 this 指向的,就是改變函數執行時的上下文。
修改上述面試題:
var name = 'window name'
var p1 = {
name:'p1 name',
showName:function(){
console.info(this.name) // p2 name
}
}
var p2 = {
name:'p2 name',
}
p1.showName.call(p2)
p1.showName.apply(p2)
var bind = p1.showName.bind(p2)
bind()
call 、bind 和 apply 改變 this 指向,最大作用就是實現代碼復用。
至于 call、bind 和 apply 的區別,可以自行去了解下。
4、隱式轉化
console.log( '2'>10 ) //false
console.log( '2'>'10' ) //true
console.log( 'abc'>'b' ) //false
console.log( 'abc'>'aab' ) //true
console.log( undefined == null ) //true
console.log( NaN == NaN )//false
console.log( [] == 0 ) //true
console.log( ![] == 0 ) //true
console.log( [] == [] ) //false
console.log( {} == {} ) //false
console.log( {} == !{} ) //false
對象的類型轉換表
有了上邊那個表,事情就變得簡單了!
關系運算符進行運算時,不同類型的值會自動轉化為相同類型值,然后進行
1、兩邊有一個是字符串一個是是數字時,字符串調用 Number 方法,將字符串轉為數字,所以:
console.log( '2'>10 ) => console.log( 2>10 )
2、如果兩邊都是字符串時,按照字符串的 unicode 編碼來轉換的,所以:
'2'.charCodeAt() = 50
'10'.charCodeAt() = 49
console.log( '2'>'10' ) => console.log( 50 >49 )
3、字符串進項比較時,先比較第一位,如果不相等直接得出結果,如果第一項相等,會繼續使用第二項進行比較。
console.log( 'abc'>'b' ) // a < b 所以為 false
console.log( 'abc'>'aab' ) // a=a 第二位 b>a 所以為 true
4、轉為布爾值都為 false 的類型分別有:undefined 、null 、0、NaN、false、‘’
console.log( undefined == null ) //true
5、NaN表示的是非數字,但是這個非數字也是不同的,因此 NaN 不等于 NaN,兩個NaN永遠不可能相等。
console.log( NaN == NaN )//false
6、關系運算有一個數值,將另外一個值也轉為 number 類型。
Number([].valueOf().toString()) = 0
console.log( [] == 0 ) => console.log( 0 == 0 ) //true
7、有邏輯運算的,將其他數據類型轉為 boolean 值。
Boolean([]) = true => 取反 true
console.log( ![] == 0 ) => console.log( false == false ) //true
8、直接使用兩個空數組比較,數組地址不同,所以不相等。
console.log( [] == [] ) //false
// 對象地址不一樣
console.log( {} == {} ) //false
{}.valueOf().toString() ="[object Object]"
console.log( {} ==