我真的懂 this 嗎

拾夕發表於2019-04-03

先知

this 實際上是在函式被呼叫時發生的繫結,它指向哪裡完全取決於函式在哪裡被呼叫。

大提示: 不想看分析的可以直接拉到最後看結論。

面試常見題

第一題

function foo(){
  console.log(this.a)
}
var obj = {
  a :2,
  foo:foo
}
var bar = obj.foo
obj.foo() 
bar()  
複製程式碼

問: 最後兩個列印出什麼?

第二題

function foo(){
  console.log(this.a)
}
var obj = {
  a :2,
  foo:foo
}
var obj2 = {
  a:1,
  obj:obj
}
var obj3 = obj2.obj.foo
obj2.obj.foo() // 2
obj3() //undefined
複製程式碼

問: 最後兩個列印出什麼? 答?

我們如何理解 this 繫結的到底是誰?或者說 this 到底是誰?下面介紹四種方法

1. 預設繫結

什麼是預設繫結呢?上程式碼先

var a = 2;
function foo(){
  console.log(this.a)
}
function foo2(){
  'use strict'
  console.log(this.a)
}
foo() // 2
foo2() //  TypeError: Cannot read property 'a' of undefined
複製程式碼
  • 在上面的程式碼中,「 foo() 直接使用時不帶任何修飾的函式引用進行呼叫的 」, 因此只能使用「預設繫結」
  • 所以到 foo() 中的 this 繫結到「 全域性物件 window 」,而「 嚴格模式 」時,全域性物件無法被繫結,所以 this 繫結到 「undefined」

預設繫結總結:預設繫結即不帶任何修飾的函式引用被呼叫時的繫結,此時 this 繫結到「 全域性物件 window 」或者 「undefined」

2. 隱式繫結

隱式繫結是什麼呢?隱式的繫結

當函式引用有上下文物件時,隱式繫結會把函式呼叫中的 this 繫結到這個上下文物件。

那麼回到面試題

2.1 面試題一

function foo(){ console.log(this.a)}
var obj = { a :2,  foo:foo }
var bar = obj.foo
obj.foo() //2
bar()  /undefined

複製程式碼

foo() 被呼叫時,落腳點指向 obj 物件(上下文物件),所以 this 繫結到 obj ,this.a 即 obj.a , 所以列印出了 2

但是為什麼 bar() 列印出了 undefined 呢?

雖然 bar 是 obj.foo 的引用,但實際上是 foo函式 本身的引用,所以此時 bar() 是不帶任何修飾的函式呼叫,使用預設呼叫

2.2 面試題二

function foo(){ console.log(this.a)  }
var obj = {  a :2,  foo:foo  }
var obj2 = {  a:1,  obj:obj   }
var obj3 = obj2.obj.foo
obj2.obj.foo() // 2
obj3() //undefined
複製程式碼

跟上面一題相同,雖然引用鏈比較長,但是最後 foo() 是在 obj 中被呼叫,所以 this 繫結到 obj ,引用鏈只有最後一層影響呼叫位置。

隱式繫結總結 : 函式在上下文物件中呼叫時, this 繫結到 上下文物件上。

3. 顯式繫結

使用「 call apply bind 」 進行繫結 this , this 繫結到第一個傳入的引數

MDN三者的用法

3.1 call 的使用

回到面試題第一題,我們使用「 call 」進行顯式繫結

function foo(){ console.log(this.a)  }
var obj = {
  a :2,
  foo:foo
}
var bar =obj.foo
obj.foo() // 2
bar.call(obj) //2
複製程式碼

此時兩者都列印出 2 ,因為我們將 this 繫結到 obj ,列印出的即是 //obj.a //2

3.2 apply 繫結第一個引數是 this , 二參是 一個陣列

3.3 bind 是 繫結第一個引數是 this

顯式繫結總結: 使用call apply bind 時,第一個引數是 this ,不傳的話,預設為 undefined 。

4. new 繫結

使用 new 來呼叫函式, 到底做了什麼 ?
    1. 建立一個全新的物件
    1. 這個新物件的__proto__ 連結到 建構函式的 prototype
    1. 這個新物件會繫結到函式呼叫的 this
    1. 如果函式沒有返回其他物件,那麼 new 表示式中的函式呼叫會自動返回這個新物件。
function foo(a){
  this.a = a 
}
var bar = new foo(2)

console.log(bar.a) // 2
複製程式碼

分析: 使用 new 呼叫 foo() 時,我們構造一個物件並把它繫結到 foo() 呼叫的 this 上

####new 繫結總結: new 繫結中 this 繫結的就是新生成的物件

總結

    1. 「 箭頭函式 」內的 this 就是外面的 this ,外面的 this 是啥看下面四條
  • 2 . 「 new 繫結 」函式是否在 new 中呼叫 ? 如果是 this 繫結的即是 新建立的物件。 var bar = new foo()

    1. 「 顯式繫結 」函式是否通過 call 、apply、 bind 繫結?this繫結的時第一個引數 var bar = foo.call(obj)
  • 4.「 隱式繫結 」 函式是否在某個上下文中呼叫? 是的話,this 繫結的是那個上下文物件 var bar = obj.foo()

    1. 「 預設繫結」如果都不是,那麼就是預設繫結。嚴格模式繫結到 undefined ,否則繫結到 全域性物件 var bar = foo()

後記

你要是看不懂我也沒辦法了,我的修為都在這了。。。。

文章為個人總結,不足之處還請留言或私信。

轉載請註明出處。

以上。

相關文章