【前端面試之道小冊筆記】JS基礎篇No.1
1、數據類型
原始值:boolean、null、undefined、number、string、symbol
2、類型轉換:
轉Boolean
在條件判斷時,除了 undefined
, null
, false
, NaN
, ‘‘
, 0
, -0
,其他所有值都轉為 true
,包括所有對象。
對象轉原始類型
對象在轉換類型的時候,會調用內置的 [[ToPrimitive]]
函數,對於該函數來說,算法邏輯一般來說如下:
- 如果已經是原始類型了,那就不需要轉換了
- 如果需要轉字符串類型就調用
x.toString()
,轉換為基礎類型的話就返回轉換的值。不是字符串類型的話就先調用valueOf
toString
- 調用
x.valueOf()
,如果轉換為基礎類型,就返回轉換的值 - 如果都沒有返回原始類型,就會報錯
當然你也可以重寫 Symbol.toPrimitive
,該方法在轉原始類型時調用優先級最高。
let a = {
valueOf() {
return 0
},
toString() {
return ‘1‘
},
[Symbol.toPrimitive]() {
return 2
}
}
1 + a // => 3
四則運算符
加法運算符不同於其他幾個運算符,它有以下幾個特點:
- 運算中其中一方為字符串,那麽就會把另一方也轉換為字符串
- 如果一方不是字符串或者數字,那麽會將它轉換為數字或者字符串
1 + ‘1‘ // ‘11‘
true + true // 2
4 + [1,2,3] // "41,2,3"
如果你對於答案有疑問的話,請看解析:
- 對於第一行代碼來說,觸發特點一,所以將數字
1
轉換為字符串,得到結果‘11‘
- 對於第二行代碼來說,觸發特點二,所以將
true
轉為數字1
- 對於第三行代碼來說,觸發特點二,所以將數組通過
toString
轉為字符串1,2,3
,得到結果41,2,3
另外對於加法還需要註意這個表達式 ‘a‘ + + ‘b‘
‘a‘ + + ‘b‘ // -> "aNaN"
因為 + ‘b‘
等於 NaN
,所以結果為 "aNaN"
,你可能也會在一些代碼中看到過 + ‘1‘
的形式來快速獲取 number
類型。
那麽對於除了加法的運算符來說,只要其中一方是數字,那麽另一方就會被轉為數字
4 * ‘3‘ // 12
4 * [] // 0
4 * [1, 2] // NaN
3、this
我們先來看幾個函數調用的場景
function foo() {
console.log(this.a)
}
var a = 1
foo()
const obj = {
a: 2,
foo: foo
}
obj.foo()
const c = new foo()
接下來我們一個個分析上面幾個場景
- 對於直接調用
foo
來說,不管foo
函數被放在了什麽地方,this
一定是window
- 對於
obj.foo()
來說,我們只需要記住,誰調用了函數,誰就是this
,所以在這個場景下foo
函數中的this
就是obj
對象 - 對於
new
的方式來說,this
被永遠綁定在了c
上面,不會被任何方式改變this
說完了以上幾種情況,其實很多代碼中的 this
應該就沒什麽問題了,下面讓我們看看箭頭函數中的 this
function a() {
return () => {
return () => {
console.log(this)
}
}
}
console.log(a()()())
首先箭頭函數其實是沒有 this
的,箭頭函數中的 this
只取決包裹箭頭函數的第一個普通函數的 this
。在這個例子中,因為包裹箭頭函數的第一個普通函數是 a
,所以此時的 this
是 window
。另外對箭頭函數使用 bind
這類函數是無效的。
最後種情況也就是 bind
這些改變上下文的 API 了,對於這些函數來說,this
取決於第一個參數,如果第一個參數為空,那麽就是 window
。
那麽說到 bind
,不知道大家是否考慮過,如果對一個函數進行多次 bind
,那麽上下文會是什麽呢?
let a = {}
let fn = function () { console.log(this) }
fn.bind().bind(a)() // => ?
如果你認為輸出結果是 a
,那麽你就錯了,其實我們可以把上述代碼轉換成另一種形式
// fn.bind().bind(a) 等於
let fn2 = function fn1() {
return function() {
return fn.apply()
}.apply(a)
}
fn2()
可以從上述代碼中發現,不管我們給函數 bind
幾次,fn
中的 this
永遠由第一次 bind
決定,所以結果永遠是 window
。
let a = { name: ‘yck‘ }
function foo() {
console.log(this.name)
}
foo.bind(a)() // => ‘yck‘
以上就是 this
的規則了,但是可能會發生多個規則同時出現的情況,這時候不同的規則之間會根據優先級最高的來決定 this
最終指向哪裏。
首先,new
的方式優先級最高,接下來是 bind
這些函數,然後是 obj.foo()
這種調用方式,最後是 foo
這種調用方式,同時,箭頭函數的 this
一旦被綁定,就不會再被任何方式所改變。
如果你還是覺得有點繞,那麽就看以下的這張流程圖吧,圖中的流程只針對於單個規則。
補充思考:call&apply&bind
1、call() 、apply()可以看作是某個對象的方法,通過調用方法的形式來間接調用函數,讓函數在某個指定的對象下執行。bind() 就是將某個函數綁定到某個對象上
2、bind() 方法會返回執行上下文被改變的函數而不會立即執行,而前兩者是直接執行該函數
3、bind雖然只返回一個函數並不調用,但是仍然可以傳參,使用bind()方法使函數擁有預設的初始參數,這些參數會排在最前面,傳給綁定函數的參數會跟在它們後面
1 function list(){ 2 // 讓類數組arguments擁有數組的方法slice,這個函數實現了簡單把類數組轉換成數組 3 return Array.prototype.slice.call(arguments); 4 } 5 6 list(1,2,3);//[1,2,3] 7 8 //給list綁定一個預設參數4 9 var list1 = list.bind(undefined,4); 10 11 list1();//[4] 12 13 list1(1,2,3);//[4,1,2,3]
【前端面試之道小冊筆記】JS基礎篇No.1