this的設計及綁定規則

每個函數的this是在調用時被綁定的,完全取決於函數的調用位置;

什麼是調用位置呢?

顧名思義就是“函數被調用的位置”;要去尋找被調用的位置,那麼就要分析調用棧,this的調用位置就在當前正在執行的函數的前一個調用中;

舉個例子

function baz() {
    // 當前調用棧是全局 -> baz,調用位置是上一個執行棧就是全局作用域
    bar() // bar的調用位置
}
function bar() {
    // 當前調用棧是全局 -> baz -> bar,調用位置是上一個執行棧就是baz
    foo() // foo的調用位置
}
function foo() {
    // 當前調用棧是全局 -> baz -> bar -> foo,調用位置是上一個執行棧就是bar
}
baz() // baz的調用位置
 
接下來我們看看再函數的執行過程中調用位置是如何決定this的綁定對象的。
this綁定有四條規則,分別是:
  a.默認綁定
  b.隱式綁定
  c.顯式綁定
  d.new綁定
四種規則分別從弱到強,new綁定優先級最高,依次類推;
 
默認綁定
可以把這條規則是無法應用其他規則時的默認規則;
function foo() {
    // this指向全局
    console.log(this.a)
}
a = 1;
foo();  // 輸出1
這裏由於foo函數時直接引用進行調用的,只能使用默認綁定,無法應用其他規則,所以this指向全局對象,而全局變量a爲1,所以輸出1;
需要注意的是:再嚴格模式下,this會綁定到undefined,而不是全局對象;
 
隱式綁定
調用位置是否有上下文對象是隱式綁定需要考慮的規則,或者說是否被某個對象引用;
function foo() {
    console.log(this.a)
}
var obj = {
    a: 1,
    foo: foo
}
obj.foo(); // 輸出1
可以看到,obj對象的foo屬性添加了foo函數的引用,當foo被調用時,指向了obj對象,當函數引用有上下文對象時,隱式綁定規則會把this綁定到這個上下文對象,即obj對象;
注意:對象屬性引用鏈中只有最後一層會影響調用位置,舉個例子:

function foo() {
    console.log(this.a)
}
var obj1 = {
    a: 1,
    obj2: obj2
}
var obj2 = {
    a: 2,
    foo: foo
}
obj1.obj2.foo(); // 輸出2
 
顯式綁定
其實這種方法很常見,就是通過call/apply/bind等強綁定,可以直接指定this的綁定對象。這裏就不展開描述了
 
new綁定
相信大家對new構造函數調用都耳熟能詳,使用new調用函數,對自動執行下面的操作:
  a.創建一個全新的對象;
  b.這個新對象會被執行原型連接;
  c.這個新對象會被綁定到函數調用的this;
  d.如果函數沒有返回其他對象,那麼new表達式中的函數調用會自動返回這個新對象;
看以下代碼
function foo() {
    this.a = a;
}
var bar = new foo(2);
console.log(bar.a); // 2
使用new來調用foo()時,會構造一個新對象並把它綁定到foo()調用中的this上。
 
接下來總結一下如何判斷this
  1、函數是否再new中調用?是的話this綁定的是新創建的對象;
  2、函數是否通過call/apply調用?是的話this綁定的是指定的對象;
  3、函數是否在某個上下文對象中調用?是的話this綁定的是那個上下文對象;
  4、如果都不是以上,使用默認綁定,如果在嚴格模式下,被綁定到undefined,否則就綁定到全局對象;
以上就是this的綁定原理啦~
 
ES6 中介紹了一種無法使用這些規則的特殊函數類型:箭頭函數
箭頭函數並不會使用this的四條綁定規則,而是根據當前的詞法作用域來決定this,具體來說,箭頭函數會繼承外層函數調用的this綁定(無論this綁定到什麼),這和之前代碼中的self = this機制一樣;
 
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章