久久久久久久视色,久久电影免费精品,中文亚洲欧美乱码在线观看,在线免费播放AV片

<center id="vfaef"><input id="vfaef"><table id="vfaef"></table></input></center>

    <p id="vfaef"><kbd id="vfaef"></kbd></p>

    
    
    <pre id="vfaef"><u id="vfaef"></u></pre>

      <thead id="vfaef"><input id="vfaef"></input></thead>

    1. 站長資訊網(wǎng)
      最全最豐富的資訊網(wǎng)站

      帶你詳解 this 的四個綁定規(guī)則

      帶你詳解 this 的四個綁定規(guī)則

      前端(vue)入門到精通課程:進入學習
      Apipost = Postman + Swagger + Mock + Jmeter 超好用的API調(diào)試工具:點擊使用

      1. 關于 this 的簡單介紹

      this 關鍵字是 JavaScript 中最復雜的機制之一。它是一個很特別的關鍵字,被自動定義在所有函數(shù)的作用域中。但是即使是非常有經(jīng)驗的 JavaScript 開發(fā)者也很難說清它到底指向什么。

      任何足夠先進的技術(shù)都和魔法無異。— Arthur C. Clarke

      實際上,JavaScript 中 this 的機制并沒有那么先進,但是開發(fā)者往往會把理解過程復雜化, 毫無疑問,在缺乏清晰認識的情況下,this 對你來說完全就是一種魔法。

      2. 為什么使用 this?

      const obj = {   title: '掘金',   reading() {     console.log(this.title + ',一個幫助開發(fā)者成長的社區(qū)');   } }
      登錄后復制

      this 提供了一種更優(yōu)雅的方式來隱式“傳遞”一個對象引用,因此可以將 API 設計得更加簡潔并且易于復用。

      隨著你的使用模式越來越復雜,顯式傳遞上下文對象會讓代碼變得越來越混亂,使用 this 則不會這樣。當我們介紹對象和原型時,你就會明白函數(shù)可以自動引用合適的上下文對象有多重要

      3. 關于this 的常見的誤解

      人們很容易把 this 理解成指向函數(shù)自身,JavaScript 的新手開發(fā)者通常會認為,既然把函數(shù)看作一個對象(JavaScript 中的所有函數(shù) 都是對象),那就可以在調(diào)用函數(shù)時存儲狀態(tài)(屬性的值)。但結(jié)果通常讓他們大吃一驚,例如下面這段代碼

      function foo() {   // 讓新添加的 count + 1   this.count++ }  // 向函數(shù)對象 foo 添加了一個屬性 count foo.count = 0  foo()  console.log(foo.count);   // 0
      登錄后復制

      這段代碼看起來沒什么問題,但請把目光聚焦到最后一行,輸出 foo.count 的結(jié)果竟然是0 ?!

      疑問:為什么會這樣?我明明向函數(shù)對象 foo 添加了一個屬性 count,并且函數(shù)內(nèi)部也寫了 this.count++,為什么最后還是0呢?

      解答:因為this.count 中此時的 this,根本不是指向 foo 函數(shù)自身,而是指向全局 window。再細心一點,我們可以發(fā)現(xiàn),在 window 身上,被添加了個 count 屬性,值為 NaN。 (為什么 this 指向 window 后面會闡述)

      帶你詳解 this 的四個綁定規(guī)則所以,單純的把 this 理解為指向函數(shù)自身是錯誤的。

      this 實際上是在函數(shù)被調(diào)用時發(fā)生的綁定,它指向什么完全取決于函數(shù)在哪里被調(diào)用。

      4. this 的綁定規(guī)則

      我們來看看在函數(shù)的執(zhí)行過程中調(diào)用位置如何決定 this 的綁定對象。

      找到調(diào)用位置,然后判斷需要應用下面四條規(guī)則中的哪一條。我首先會分別解釋 這四條規(guī)則,然后解釋多條規(guī)則都可用時它們的優(yōu)先級如何排列。

      4.1 默認綁定

      首先要介紹的是最常用的函數(shù)調(diào)用類型:獨立函數(shù)調(diào)用??梢园堰@條規(guī)則看作是無法應用其他規(guī)則時的默認規(guī)則。

      function foo() {   console.log(this.a) } var a = 2 foo() // 2
      登錄后復制

      • 我們可以看到當調(diào)用 foo() 時,this.a 被解析成了全局變量 a。為什么?因為在本例中,函數(shù)調(diào)用時應用了 this 的默認綁定,因此 this 指向全局對象。

      • 那么我們怎么知道這里應用了默認綁定呢?可以通過分析調(diào)用位置來看看 foo() 是如何調(diào) 用的。在代碼中,foo() 是直接使用不帶任何修飾的函數(shù)引用進行調(diào)用的,因此只能使用默認綁定,無法應用其他規(guī)則

      • 這條規(guī)則也解釋了上面 count 的代碼中,為什么函數(shù)里面的this指向了window,因為 foo屬于獨立函數(shù)調(diào)用的,觸發(fā)了默認綁定,從而指向全局window。(瀏覽器中全局是 window對象,node 中是空對象{})

      • 屬于默認綁定規(guī)則的還有:

        • 函數(shù)調(diào)用鏈(一個函數(shù)又調(diào)用另外一個函數(shù))
        • 將函數(shù)作為參數(shù),傳入到另一個函數(shù)中

      (擴展:如果使用嚴格模式(strict mode),則不能將全局對象用于默認綁定,因此 this 會綁定到 undefined:)

      結(jié)論:默認綁定的 this,都指向全局。

      4.2 隱式綁定

      4.2.1 一般的對象調(diào)用

      這一條需要考慮的規(guī)則是調(diào)用位置是否有上下文對象,或者說是通過某個對象發(fā)起的函數(shù)調(diào)用

      function foo() {   console.log(this.a) }  const obj = {   a: 2,   foo: foo }  // 通過 obj 對象調(diào)用 foo 函數(shù) obj.foo() // 2
      登錄后復制

      • 調(diào)用位置會使用 obj 上下文來引用函數(shù),因此你可以說函數(shù)被調(diào)用時 obj 對象“擁 有”或者“包含”它。

      • foo() 被調(diào)用時,它的前面確實加上了對 obj 的引用。當函數(shù)引用有上下文對象時,隱式綁定規(guī)則會把函數(shù)調(diào)用中的 this 綁定到這個上下文對象。因為調(diào)用 foo()this 被綁定到 obj 上,因此 this.aobj.a 是一樣的。

      4.2.2 對象屬性引用鏈

      對象屬性引用鏈中只有上一層或者說最后一層在調(diào)用位置中起作用。舉例來說:

      function foo() {   console.log(this.a) }  var obj2 = {   a: 2,   foo: foo }  var obj1 = {   a: 1,   obj2: obj2 }  obj1.obj2.foo() // 2
      登錄后復制

      最終 this 指向的是 obj2

      4.2.3 隱式丟失

      一個最常見的 this 綁定問題就是被隱式綁定的函數(shù)會丟失綁定對象,也就是說它會應用默認綁定,從而把 this 綁定到全局對象或者 undefined 上(取決于是否是嚴格模式)

      第一種情況:將對象里的函數(shù)賦值給一個變量

      function foo() {   console.log(this.a) }  var obj = {   a: 2,   foo: foo }  var bar = obj.foo // 函數(shù)別名!  var a = 'global' // a 是全局對象的屬性 bar() // "global"
      登錄后復制

      雖然 barobj.foo 的一個引用,但是實際上,它引用的是 foo 函數(shù)本身,因此此時的 bar() 其實是一個不帶任何修飾的函數(shù)調(diào)用,因此應用了默認綁定

      第二種情況:傳入回調(diào)函數(shù)時

      function foo() {   console.log(this.a) }  function doFoo(fn) {   // fn 其實引用的是 foo   fn() // <-- 調(diào)用位置! }  var obj = {   a: 2,   foo: foo }  var a = 'global'  // a 是全局對象的屬性 doFoo(obj.foo)    // "global"
      登錄后復制

      參數(shù)傳遞其實就是一種隱式賦值,因此我們傳入函數(shù)時也會被隱式賦值,所以結(jié)果和上一 個例子一樣。

      結(jié)論:隱式綁定的 this,指向調(diào)用函數(shù)的上下文對象。

      4.3 顯式綁定

      4.3.1 使用 call(…) 和 apply(…)

      如果我們不想在對象內(nèi)部包含函數(shù)引用,而想在某個對象上強制調(diào)用函數(shù),該怎么做呢?可以使用函數(shù)的 call(..)apply(..) 方法

      • JavaScript 提供的絕大多數(shù)函數(shù)以及你自 己創(chuàng)建的所有函數(shù)都可以使用 call(..)apply(..) 方法。

      這兩個方法是如何工作的呢?它們的第一個參數(shù)是一個對象,是給 this 準備的,接著在調(diào)用函數(shù)時將其綁定到 this。因為你可以直接指定 this 的綁定對象,因此我們稱之為顯式綁定。 思考以下代碼:

      function foo() {   console.log(this.a) }  var obj = {   a: 2 }  foo.call(obj) // 2
      登錄后復制

      • 通過 foo.call(..),我們可以在調(diào)用 foo 時強制把它的 this 綁定到 obj 上。

      • 如果你傳入了一個原始值(字符串類型、布爾類型或者數(shù)字類型)來當作 this 的綁定對 象,這個原始值會被轉(zhuǎn)換成它的對象形式(也就是 new String(..)、new Boolean(..) 或者 new Number(..))。這通常被稱為“裝箱”。

      從 this 綁定的角度來說,call(..) 和 apply(..) 是一樣的,它們的區(qū)別體現(xiàn)在參數(shù)上:第一個參數(shù)是相同的,后面的參數(shù),call為參數(shù)列表,apply為數(shù)組,他們內(nèi)部的實現(xiàn)原理也不難理解,詳細請看以下兩個手寫方法

      • 手寫 call 方法 超級詳細 ⚡⚡⚡

      • 手寫 apply 方法 超級詳細 ⚡⚡⚡

      4.3.2 硬綁定(一個函數(shù)總是顯示的綁定到一個對象上)

      由于硬綁定是一種非常常用的模式,所以 ES5 提供了內(nèi)置的方法 Function.prototype.bind, 它的用法如下

      function foo(num) {   console.log(this.a, num)   return this.a + num }  var obj = {   a: 2 }  // 調(diào)用 bind() 方法,返回一個函數(shù) var bar = foo.bind(obj)  var b = bar(3) // 2 3  console.log(b) // 5
      登錄后復制

      調(diào)用 bind(...) 方法,會返回一個新函數(shù),那么這個新函數(shù)的 this,永遠指向我們傳入的obj對象

      關于 bind 方法的簡單實現(xiàn),可以前往:手寫 bind 方法,超級詳細 ⚡⚡⚡

      4.3.3 API調(diào)用的 “上下文(內(nèi)置函數(shù))

      第三方庫的許多函數(shù),以及 JavaScript 語言和宿主環(huán)境中許多新的內(nèi)置函數(shù),都提供了一個可選的參數(shù),通常被稱為“上下文”(context),其作用和 bind(..) 一樣,確保你的回調(diào)函數(shù)使用指定的 this。例如:

      (1)數(shù)組方法 forEach()

      function foo(el) {   console.log(el, this.id) }  var obj = {   id: 'bin' };  [1, 2, 3].forEach(foo, obj)  // 輸出: // 1 bin  // 2 bin  // 3 bin
      登錄后復制

      • 調(diào)用 foo(..) 時把 this 綁定到 obj 身上

      (2)setTimeout()

      setTimeout(function() {   console.log(this); // window }, 1000);
      登錄后復制

      • 在使用 setTimeout 時會傳入一個回調(diào)函數(shù),而這個回調(diào)函數(shù)中的this一般指向 window,這個和 setTimeout 源碼的內(nèi)部調(diào)用有關,這個不再展開贅述

      結(jié)論:顯式綁定的 this,指向我們指定的綁定對象。

      4.4 new 綁定

      在 JavaScript 中,普通函數(shù)可以使用 new 操作符去調(diào)用,此時的普通函數(shù)則被稱為 “構(gòu)造函數(shù)”。沒錯,凡是由 new 操作符調(diào)用的函數(shù),都稱為 “構(gòu)造函數(shù)”

      使用 new 來調(diào)用函數(shù),或者說發(fā)生構(gòu)造函數(shù)調(diào)用時,會自動執(zhí)行下面的操作。

      • 在內(nèi)存中創(chuàng)建一個新對象。

      • 這個新對象內(nèi)部的[[Prototype]] 特性被賦值為構(gòu)造函數(shù)的 prototype 屬性。

      • 構(gòu)造函數(shù)內(nèi)部的this 被賦值為這個新對象(即this 指向新對象)。

      • 執(zhí)行構(gòu)造函數(shù)內(nèi)部的代碼(給新對象添加屬性)。

      • 如果構(gòu)造函數(shù)返回非空對象,則返回該對象;否則,返回剛創(chuàng)建的新對象。

      function foo(a) {   this.a = a }  var bar = new foo(2)  console.log(bar.a) // 2
      登錄后復制

      使用 new 來調(diào)用 foo(..) 時,我們會構(gòu)造一個新對象并把它綁定到 foo(..) 調(diào)用中的 this 上。new 是最后一種可以影響函數(shù)調(diào)用時 this 綁定行為的方法,我們稱之為 new 綁定。

      結(jié)論:new 綁定的 this,都指向通過 new 調(diào)用的函數(shù)的實例對象(就是該函數(shù))

      5. 綁定規(guī)則的優(yōu)先級

      現(xiàn)在我們已經(jīng)了解了函數(shù)調(diào)用中 this 綁定的四條規(guī)則,你需要做的就是找到函數(shù)的調(diào)用位置并判斷應當應用哪條規(guī)則。但是,如果某個調(diào)用位置可以應用多條規(guī)則呢?所以就需要有綁定規(guī)則的優(yōu)先級。

      它們之間的優(yōu)先級關系為:

      默認綁定 < 隱式綁定 < 顯示綁定(bind) < new綁定

      這里提前列出優(yōu)先級,想看詳細代碼解析的可以往下看,也可以直接拖到最后面的例題部分

      5.1 默認綁定的優(yōu)先級最低

      毫無疑問,默認規(guī)則的優(yōu)先級是最低的,因為存在其他規(guī)則時,就會通過其他規(guī)則的方式來綁定this

      5.2 隱式綁定和顯式綁定的優(yōu)先級比較

      測試一下即可知道,有以下代碼:

      function foo() {   console.log(this.a) }  var obj1 = {   a: 1,   foo: foo }  var obj2 = {   a: 2,   foo: foo }  // 同時使用隱式綁定和顯示綁定 obj1.foo.call(obj2) // 2
      登錄后復制

      可以看到,輸出的結(jié)果為 2,說明 foo 函數(shù)內(nèi) this 指向的是 obj2,而 obj2 是通過顯示綁定調(diào)用的,所以:顯示綁定的優(yōu)先級更高

      5.3 隱式綁定和 new 綁定的優(yōu)先級比較

      有以下測試代碼:

      function foo() {   console.log(this); }  var obj = {   title: "juejin",   foo: foo }  // 同時使用隱式綁定和new綁定 new obj.foo(); // foo對象
      登錄后復制

      最后 foo 函數(shù)輸出的 this 為 foo 對象,說明new綁定優(yōu)先級更高(否則應該輸出 obj 對象),所以:new 綁定的優(yōu)先級更高

      5.4 new 綁定和顯示綁定的優(yōu)先級比較

      最后,new 綁定和顯式綁定誰的優(yōu)先級更高呢?

      new綁定和call、apply是不允許同時使用的,只能和 bind 相比較,如下:

      function foo() {   console.log(this) }  var obj = {   title: "juejin" }  var foo = new foo.call(obj);  // 直接報錯
      登錄后復制

      但是 new 綁定可以和 bind 方法返回后的函數(shù)一起使用

      function foo() {   console.log(this); }  var obj = {   title: "juejin" }  var bar = foo.bind(obj);  var foo = new bar(); // foo 對象, 說明使用的是new綁定
      登錄后復制

      最后 foo 函數(shù)輸出的 this 為 foo 對象,說明new綁定優(yōu)先級更高(否則應該輸出 obj 對象),所以:new 綁定的優(yōu)先級更高

      優(yōu)先級結(jié)論:默認綁定 < 隱式綁定 < 顯示綁定(bind)< new綁定

      6. 判斷this

      現(xiàn)在我們可以根據(jù)優(yōu)先級來判斷函數(shù)在某個調(diào)用位置應用的是哪條規(guī)則。可以按照下面的 順序來進行判斷:

      • 函數(shù)是否在 new 中調(diào)用(new 綁定)?如果是的話 this 綁定的是新創(chuàng)建的對象。 var bar = new foo()

      • 函數(shù)是否通過 call、apply(顯式綁定)或者硬綁定調(diào)用?如果是的話,this 綁定的是 指定的對象。 var bar = foo.call(obj2)

      • 函數(shù)是否在某個上下文對象中調(diào)用(隱式綁定)?如果是的話,this 綁定的是那個上 下文對象。 var bar = obj1.foo()

      • 如果都不是的話,使用默認綁定。如果在嚴格模式下,就綁定到 undefined,否則綁定 到全局對象。 var bar = foo()

      就是這樣。對于正常的函數(shù)調(diào)用來說,理解了這些知識你就可以明白 this 的綁定原理了。 不過……凡事總有例外

      7. 綁定例外

      規(guī)則總有例外,這里也一樣。在某些場景下 this 的綁定行為會出乎意料,你認為應當應用其他綁定規(guī)則時,實際上應用的可能是默認綁定規(guī)則。

      7.1 箭頭函數(shù)

      箭頭函數(shù)不使用 this 的四種標準規(guī)則,而是根據(jù)外層(函數(shù)或者全局)作用域來決定 this。

      7.2 被忽略的this

      如果你把 null 或者 undefined 作為 this 的綁定對象傳入 call、apply 或者 bind,這些值在調(diào)用時會被忽略,實際應用的是默認綁定規(guī)則:

      function foo() {   console.log(this.a) }  var a = 2  foo.call(null) // 2 foo.call(undefined) // 2 foo.bind(null)();
      登錄后復制

      最后輸出的結(jié)果都是 2,說明 this 指向的是全局 window

      7.3 間接引用

      另一個需要注意的是,你有可能(有意或者無意地)創(chuàng)建一個函數(shù)的間接引用,在這種情況下,調(diào)用這個函數(shù)會應用默認綁定規(guī)則。 間接引用最容易在賦值時發(fā)生:

      function foo() {   console.log(this.a) } var a = 2 var o = { a: 3, foo: foo } var p = { a: 4 }  o.foo(); // 3  // 函數(shù)賦值 (p.foo = o.foo)()  // 2
      登錄后復制

      賦值表達式 p.foo = o.foo 的返回值是目標函數(shù)的引用,因此調(diào)用位置是 foo() 屬于獨立函數(shù)調(diào)用,而不是 p.foo() 或者 o.foo()。根據(jù)我們之前說過的,這里會應用默認綁定。

      8. this 判斷例題

      請說出例題中的輸出結(jié)果

      8.1 例題一

      var name = "window"; var person = {   name: "person",   sayName: function () {     console.log(this.name);   } }; function sayName() {   var sss = person.sayName;   sss();    person.sayName();    (person.sayName)();    (b = person.sayName)();  } sayName();
      登錄后復制

      解析:

      function sayName() {   var sss = person.sayName;   // 獨立函數(shù)調(diào)用,沒有和任何對象關聯(lián)   sss(); // window   // 關聯(lián)   person.sayName(); // person   (person.sayName)(); // person   (b = person.sayName)(); // window }
      登錄后復制

      8.2 例題二

      var name = 'window' var person1 = {   name: 'person1',   foo1: function () {     console.log(this.name)   },   foo2: () => console.log(this.name),   foo3: function () {     return function () {       console.log(this.name)     }   },   foo4: function () {     return () => {       console.log(this.name)     }   } }  var person2 = { name: 'person2' }  person1.foo1();  person1.foo1.call(person2);   person1.foo2(); person1.foo2.call(person2);  person1.foo3()(); person1.foo3.call(person2)(); person1.foo3().call(person2);  person1.foo4()(); person1.foo4.call(person2)(); person1.foo4().call(person2);
      登錄后復制

      解析:

      // 隱式綁定,肯定是person1 person1.foo1(); // person1 // 隱式綁定和顯示綁定的結(jié)合,顯示綁定生效,所以是person2 person1.foo1.call(person2); // person2  // foo2()是一個箭頭函數(shù),不適用所有的規(guī)則 person1.foo2() // window // foo2依然是箭頭函數(shù),不適用于顯示綁定的規(guī)則 person1.foo2.call(person2) // window  // 獲取到foo3,但是調(diào)用位置是全局作用于下,所以是默認綁定window person1.foo3()() // window // foo3顯示綁定到person2中 // 但是拿到的返回函數(shù)依然是在全局下調(diào)用,所以依然是window person1.foo3.call(person2)() // window // 拿到foo3返回的函數(shù),通過顯示綁定到person2中,所以是person2 person1.foo3().call(person2) // person2  // foo4()的函數(shù)返回的是一個箭頭函數(shù) // 箭頭函數(shù)的執(zhí)行找上層作用域,是person1 person1.foo4()() // person1 // foo4()顯示綁定到person2中,并且返回一個箭頭函數(shù) // 箭頭函數(shù)找上層作用域,是person2 person1.foo4.call(person2)() // person2 // foo4返回的是箭頭函數(shù),箭頭函數(shù)只看上層作用域 person1.foo4().call(person2) // person1
      登錄后復制

      9. 總結(jié)

      如果要判斷一個運行中函數(shù)的 this 綁定,就需要找到這個函數(shù)的直接調(diào)用位置。找到之后就可以順序應用下面這四條規(guī)則來判斷 this 的綁定對象。

      • 由 new 調(diào)用?綁定到新創(chuàng)建的對象。

      • 由 call 或者 apply(或者 bind)調(diào)用?綁定到指定的對象。

      • 由上下文對象調(diào)用?綁定到那個上下文對象。

      • 默認:在嚴格模式下綁定到 undefined,否則綁定到全局對象。


      每文一句:如果把生活比喻為創(chuàng)作的意境,那么閱讀就像陽光。

      ok,本次的分享就到這里,如果本章內(nèi)容對你有所幫助的話可以點贊+收藏,希望大家都能夠有所收獲。有任何疑問都可以在評論區(qū)留言,大家一起探討、進步!

      【推薦學習:javascript高級教程】

      贊(0)
      分享到: 更多 (0)
      網(wǎng)站地圖   滬ICP備18035694號-2    滬公網(wǎng)安備31011702889846號