你不知道的js中關於this綁定機制的解析[看完還不懂算我輸]

前言

最近正在看《你不知道的JavaScript》,裏面關於this綁定機制的部分講的特別好,很清晰,這部分對我們js的使用也是相當關鍵的,並且這也是一個面試的高頻考點,所以整理一篇文章分享一下這部分的內容,相信看本文的解析,你一定會有所收穫的,如果喜歡的話可以點波贊/關注,支持一下。

個人博客瞭解一下:obkoro1.com


爲什麼要用this:

function identify() {
  console.log("Hello,I'm " + this.name);
}
let me = {
  name: "Kyle"
};
let you = {
  name: "Reader"
};
identify.call(me); // Hello,I'm Kyle
identify.call(you); // Hello,I'm Reader

這個簡單的栗子,可以在不同的對象中複用函數identify,不用針對每個對象編寫一個新函數。

this解決的問題:

this提供了一種更優雅的方法來隱式’傳遞’一個對象的引用,因此可以將API設計得更加簡潔並且易於複用

this的四種綁定規則:

默認綁定:

規則:在非嚴格模式下,默認綁定的this指向全局對象,嚴格模式下this指向undefined

function foo() {
  console.log(this.a); // this指向全局對象
}
var a = 2;
foo(); // 2
function foo2() {
  "use strict"; // 嚴格模式this綁定到undefined
  console.log(this.a); 
}
foo2(); // TypeError:a undefined

默認綁定規則如上述栗子,書中還提到了一個微妙的細節:

function foo() {
  console.log(this.a); // foo函數不是嚴格模式 默認綁定全局對象
}
var a = 2;
function foo2(){
  "use strict";
  foo(); // 嚴格模式下調用其他函數,不影響默認綁定
}
foo2(); // 2

所以:對於默認綁定來說,決定this綁定對象的是函數體是否處於嚴格模式,嚴格指向undefined,非嚴格指向全局對象。

通常不會在代碼中混用嚴格模式和非嚴格模式,所以這種情況很罕見,知道一下就可以了,避免某些變態的面試題挖坑。

隱式綁定:

規則:函數在調用位置,是否有上下文對象,如果有,那麼this就會隱式綁定到這個對象上。

    function foo() {
      console.log(this.a);
    }
    var a = "Oops, global";
    let obj2 = {
      a: 2,
      foo: foo
    };
    let obj1 = {
      a: 22,
      obj2: obj2
    };
    obj2.foo(); // 2 this指向調用函數的對象
    obj1.obj2.foo(); // 2 this指向最後一層調用函數的對象

    // 隱式綁定丟失
    let bar = obj2.foo; // bar只是一個函數別名 是obj2.foo的一個引用
    bar(); // "Oops, global" - 指向全局

隱式綁定丟失:

隱式綁定丟失的問題:實際上就是函數調用時,並沒有上下文對象,只是對函數的引用,所以會導致隱式綁定丟失。

同樣的問題,還發生在傳入回調函數中,這種情況更加常見,並且隱蔽,類似:

    test(obj2.foo); // 傳入函數的引用,調用時也是沒有上下文對象。

顯式綁定:

就像我們上面看到的,如果單純使用隱式綁定肯定沒有辦法得到期望的綁定,幸好我們還可以在某個對象上強制調用函數,從而將this綁定在這個函數上

規則:我們可以通過applycallbind將函數中的this綁定到指定對象上。

function foo() {
    console.log(this.a);
}
let obj = {
    a: 2
};
foo.call(obj); // 2

傳入的不是對象:

如果你傳入了一個原始值(字符串,布爾類型,數字類型),來當做this的綁定對象,這個原始值轉換成它的對象形式。

如果你把null或者undefined作爲this的綁定對象傳入call/apply/bind,這些值會在調用時被忽略,實際應用的是默認綁定規則。

new綁定:

書中提到:在js中,實際上並不存在所謂的’構造函數’,只有對於函數的’構造調用’。

new的時候會做哪些事情:

  1. 創建一個全新的對象
  2. 這個新對象會被執行 [[Prototype]] 連接。
  3. 這個新對象會綁定到函數調用的this
  4. 如果函數沒有返回其他對象,那麼new表達式中的函數調用會自動返回這個新對象。

規則:使用構造調用的時候,this會自動綁定在new期間創建的對象上。

function foo(a) {
  this.a = a; // this綁定到bar上
}
let bar = new foo(2);
console.log(bar.a); // 2

this四種綁定規則的優先級

如果在某個調用位置應用了多條規則,如何確定哪條規則生效?

    obj.foo.call(obj2); // this指向obj2 顯式綁定比隱式綁定優先級高。
    new obj.foo(); // thsi指向new新創建的對象 new綁定比隱式綁定優先級高。

顯式綁定和隱式綁定無法直接比較(會報錯),默認綁定是不應用其他規則之後的兜底綁定所以優先級最低,最後的結果是:

顯式綁定 > 隱式綁定 > 默認綁定

new綁定 > 隱式綁定 > 默認綁定

箭頭函數的this指向不會使用上述的四條規則:

function foo() {
  return () => {
    console.log(this.a);
  };
}
let obj1 = {
  a: 2
};
let obj2 = {
  a: 22
};
let bar = foo.call(obj1); // foo this指向obj1
bar.call(obj2); // 輸出2 這裏執行箭頭函數 並試圖綁定this指向到obj2

從上述栗子可以得出,箭頭函數的this規則:

  1. 箭頭函數中的this繼承於它外面第一個不是箭頭函數的函數的this指向
  2. 箭頭函數的 this 一旦綁定了上下文,就不會被任何代碼改變

結語

認真看完的話,相信你已經get到this的用法了,最後推薦一下《你不知道的JavaScript》,這本書真的很好,寫的也很有趣,沒看過的小夥伴抓緊入手了。

PS:目前離職中,大佬們有坑位可以介紹一下呀,base:上海長寧。

希望看完的朋友可以點個喜歡/關注,您的支持是對我最大的鼓勵。

個人blog and 掘金個人主頁,如需轉載,請放上原文鏈接並署名。碼字不易,感謝支持!

如果喜歡本文的話,歡迎關注我的訂閱號,漫漫技術路,期待未來共同學習成長。

以上2018.6.30

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章