詳解javascript中的this的指向問題
- 2019 年 10 月 3 日
- 筆記
首先,要明白this 既不指向函數自身,也不指函數的詞法作用域。this一般存在於函數中,表示當前函數的執行上下文,如果函數沒有執行,那麼this沒有內容,只有函數在執行後this才有綁定。
然後,我們來看看this四種綁定規則,也可以說在四種不同函數執行方式時this的指向。
1.默認綁定(執行)
默認執行:即沒有其他綁定規則存在時的默認規則。這也是函數調用中最常用的規則。this指向window,嚴格模式下指向undefined
function fn(){ console.log(this) } fn() fn()列印到控制台結果為window
開啟嚴格模式後
function fn(){ "use strict" console.log(this) } fn() fn()列印到控制台結果為undefined
2.隱式綁定(執行)
通過對象執行(通過上下文對象執行) obj.fn():當前的執行對象
function fn(){ console.log(this.a) } var a = 10; var obj = { a:20, b:fn } var obj2 = { a:30, b:obj.b } obj2.b(); //?? obj2.b(); 列印到控制台結果為30
因為obj2.b();在上述程式碼中等價於obj2.fn(),所以此時this指向obj2,所以列印結果為30;
多層調用
function fn(){ console.log(this.a) } var a = 10; var obj = { a:20, b:fn } var obj2 = { a:30, b:obj.b } var obj3 = { a:40, b:obj2 } obj3.b.b() //? obj3.b.b() 列印結果為30
上述程式碼調用結果為obj3.b->obj2,obj2.b->obj.b->fn,因為this只獲取最近一層調用的上下文對象,即obj2,
所以結果為30
隱式丟失(函數別名)
注意:這裡存在一個陷阱,大家在分析調用過程時,要特別小心
function fn() { console.log( this.a ); } var a = 2; var obj = { a: 3, b: fn }; var bar = obj.b; bar(); //? bar() 列印結果為2
為什麼會這樣,obj.b 賦值給bar,那調用bar()為什麼沒有觸發隱式綁定,使用的是默認綁定呢。
這裡有個概念要理解清楚,obj.b 是引用屬性,賦值給bar的實際上就是fn函數(即:bar指向fn本身)。
那麼,實際的調用關係是:通過bar找到fn函數,進行調用。整個調用過程並沒有obj的參數,所以是默認綁定,全局屬性a。
隱士丟失(回調函數)
function fn(){ console.log(this.a) } var a=20; var obj = { a:20, b:fn } setTimeout(obj.b, 2000); function setTimeout(cb,t){ // t cb() //? } cb()列印結果為20
同樣的道理,雖然參傳是obj.fn,因為是引用關係,所以傳參實際上傳的就是fn對象本身的引用。對於setTimeout的調用,還是 setTimeout -> 獲取參數中fn的引用參數 -> 執行 fn 函數,中間沒有obj的參與。這裡依舊進行的是默認綁定。
3.顯示綁定(執行)
function fn(){ console.log(this.a) } var obj1= { a:10 } var obj2 ={ a:20 } var obj3 ={ a:30 } fn.bind(obj1)(); fn.call(obj2); fn.apply(obj3); fn.bind(obj1)();列印結果為10
fn.call(obj2); 列印結果為20
fn.apply(obj3);列印結果為30
這裡要注意fn.bind(obj1)後面還要加一個括弧才執行!
4.new 綁定(構造函數執行(通過new執行))
js中的new操作符,和其他語言中(如JAVA)的new機制是不一樣的。js中,它就是一個普通函數調用,只是被new修飾了而已。
使用new來調用函數,會自動執行如下操作:
如果函數沒有返回其他對象,那麼new表達式中的函數調用會自動返回這個新對象。
從這點可以看出,this指向的就是對象本身。
function foo(a) { this.a = a; } var a = 2; var bar1 = new foo(3); console.log(bar1.a); // ? var bar2 = new foo(4); console.log(bar2.a); // ? 因為每次調用生成的是全新的對象,該對象又會自動綁定到this上,所以列印結果分別為3,4
綁定規則優先順序
優先順序是這樣的,以按照下面的順序來進行判斷:
數是否在new中調用(new綁定)?如果是的話this綁定的是新創建的對象。
數是否通過call、apply(顯式綁定)或者硬綁定調用?如果是的話,this綁定的是 指定的對象。
數是否在某個上下文對象中調用(隱式綁定)?如果是的話,this綁定的是那個上下文對象。
果都不是的話,使用默認綁定。如果在嚴格模式下,就綁定到undefined,否則綁定到 全局對象。