gmnon.cn-疯狂蹂躏欧美一区二区精品,欧美精品久久久久a,高清在线视频日韩欧美,日韩免费av一区二区

站長資訊網
最全最豐富的資訊網站

帶你詳解 this 的四個綁定規則

帶你詳解 this 的四個綁定規則

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

1. 關于 this 的簡單介紹

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

任何足夠先進的技術都和魔法無異?!?Arthur C. Clarke

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

2. 為什么使用 this?

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

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

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

3. 關于this 的常見的誤解

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

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

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

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

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

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

this 實際上是在函數被調用時發生的綁定,它指向什么完全取決于函數在哪里被調用。

4. this 的綁定規則

我們來看看在函數的執行過程中調用位置如何決定 this 的綁定對象。

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

4.1 默認綁定

首先要介紹的是最常用的函數調用類型:獨立函數調用。可以把這條規則看作是無法應用其他規則時的默認規則。

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

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

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

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

  • 屬于默認綁定規則的還有:

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

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

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

4.2 隱式綁定

4.2.1 一般的對象調用

這一條需要考慮的規則是調用位置是否有上下文對象,或者說是通過某個對象發起的函數調用

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

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

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

4.2.2 對象屬性引用鏈

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

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 綁定問題就是被隱式綁定的函數會丟失綁定對象,也就是說它會應用默認綁定,從而把 this 綁定到全局對象或者 undefined 上(取決于是否是嚴格模式)

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

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

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

第二種情況:傳入回調函數時

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

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

結論:隱式綁定的 this,指向調用函數的上下文對象。

4.3 顯式綁定

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

4.3.3 API調用的 “上下文(內置函數)

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

(1)數組方法 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
登錄后復制

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

(2)setTimeout()

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

  • 在使用 setTimeout 時會傳入一個回調函數,而這個回調函數中的this一般指向 window,這個和 setTimeout 源碼的內部調用有關,這個不再展開贅述

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

4.4 new 綁定

在 JavaScript 中,普通函數可以使用 new 操作符去調用,此時的普通函數則被稱為 “構造函數”。沒錯,凡是由 new 操作符調用的函數,都稱為 “構造函數”

使用 new 來調用函數,或者說發生構造函數調用時,會自動執行下面的操作。

  • 在內存中創建一個新對象。

  • 這個新對象內部的[[Prototype]] 特性被賦值為構造函數的 prototype 屬性。

  • 構造函數內部的this 被賦值為這個新對象(即this 指向新對象)。

  • 執行構造函數內部的代碼(給新對象添加屬性)。

  • 如果構造函數返回非空對象,則返回該對象;否則,返回剛創建的新對象。

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

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

結論:new 綁定的 this,都指向通過 new 調用的函數的實例對象(就是該函數)

5. 綁定規則的優先級

現在我們已經了解了函數調用中 this 綁定的四條規則,你需要做的就是找到函數的調用位置并判斷應當應用哪條規則。但是,如果某個調用位置可以應用多條規則呢?所以就需要有綁定規則的優先級。

它們之間的優先級關系為:

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

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

5.1 默認綁定的優先級最低

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

5.2 隱式綁定和顯式綁定的優先級比較

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

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

可以看到,輸出的結果為 2,說明 foo 函數內 this 指向的是 obj2,而 obj2 是通過顯示綁定調用的,所以:顯示綁定的優先級更高

5.3 隱式綁定和 new 綁定的優先級比較

有以下測試代碼:

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

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

5.4 new 綁定和顯示綁定的優先級比較

最后,new 綁定和顯式綁定誰的優先級更高呢?

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

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

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

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

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

優先級結論:默認綁定 < 隱式綁定 < 顯示綁定(bind)< new綁定

6. 判斷this

現在我們可以根據優先級來判斷函數在某個調用位置應用的是哪條規則??梢园凑障旅娴?順序來進行判斷:

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

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

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

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

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

7. 綁定例外

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

7.1 箭頭函數

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

7.2 被忽略的this

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

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

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

7.3 間接引用

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

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

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

8. this 判斷例題

請說出例題中的輸出結果

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;   // 獨立函數調用,沒有和任何對象關聯   sss(); // window   // 關聯   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 // 隱式綁定和顯示綁定的結合,顯示綁定生效,所以是person2 person1.foo1.call(person2); // person2  // foo2()是一個箭頭函數,不適用所有的規則 person1.foo2() // window // foo2依然是箭頭函數,不適用于顯示綁定的規則 person1.foo2.call(person2) // window  // 獲取到foo3,但是調用位置是全局作用于下,所以是默認綁定window person1.foo3()() // window // foo3顯示綁定到person2中 // 但是拿到的返回函數依然是在全局下調用,所以依然是window person1.foo3.call(person2)() // window // 拿到foo3返回的函數,通過顯示綁定到person2中,所以是person2 person1.foo3().call(person2) // person2  // foo4()的函數返回的是一個箭頭函數 // 箭頭函數的執行找上層作用域,是person1 person1.foo4()() // person1 // foo4()顯示綁定到person2中,并且返回一個箭頭函數 // 箭頭函數找上層作用域,是person2 person1.foo4.call(person2)() // person2 // foo4返回的是箭頭函數,箭頭函數只看上層作用域 person1.foo4().call(person2) // person1
登錄后復制

9. 總結

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

  • 由 new 調用?綁定到新創建的對象。

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

  • 由上下文對象調用?綁定到那個上下文對象。

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


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

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

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

贊(0)
分享到: 更多 (0)
網站地圖   滬ICP備18035694號-2    滬公網安備31011702889846號
gmnon.cn-疯狂蹂躏欧美一区二区精品,欧美精品久久久久a,高清在线视频日韩欧美,日韩免费av一区二区
日韩有码免费视频| 国产精品视频黄色| 性欧美18一19内谢| 中日韩av在线播放| 日本成人中文字幕在线| 无码少妇一区二区三区芒果| 黄色一级在线视频| 久久国产精品网| 免费毛片网站在线观看| 国产超级av在线| 亚洲最大综合网| 一区二区三区免费播放| 中文字幕第38页| 在线视频一二区| 特大黑人娇小亚洲女mp4| 成年人网站国产| 成年人观看网站| 手机在线看福利| 日韩视频在线免费播放| 99re6这里有精品热视频| 日韩国产欧美亚洲| 国产wwwxx| 九九九久久久久久久| 97超碰国产精品| 国产天堂在线播放| 老汉色影院首页| 国产精品免费入口| 中文字幕第66页| 人妻av中文系列| 欧美日韩精品区别| 亚洲熟妇国产熟妇肥婆| 天天爽天天爽夜夜爽| xxx中文字幕| 999精品网站| 无码人妻精品一区二区蜜桃网站| 免费毛片网站在线观看| 亚洲精品综合在线观看| 国产又粗又猛又爽又黄的网站| 妞干网在线免费视频| 欧美aaa在线观看| 欧在线一二三四区| 久久av高潮av| 91福利国产成人精品播放| japanese在线播放| 亚洲综合色在线观看| 久艹在线免费观看| www.超碰97.com| 亚洲国产精品毛片av不卡在线| 亚洲高潮无码久久| 一区二区三区网址| 可以免费观看av毛片| 大陆av在线播放| 国产三级中文字幕| av噜噜在线观看| 天天干天天干天天干天天干天天干| www.国产亚洲| 欧洲金发美女大战黑人| 久热精品在线播放| 日韩精品一区二区三区不卡| 国产精品久久久久久久乖乖| 天天想你在线观看完整版电影免费| 日本a√在线观看| 激情综合网俺也去| 欧美视频第一区| 成年人视频在线免费| 91视频 -- 69xx| 日韩在线综合网| 免费看又黄又无码的网站| 国产毛片久久久久久国产毛片| 香蕉视频xxxx| 黄色一级片国产| 国产69精品久久久久999小说| 国产欧美日韩小视频| 日本男女交配视频| 高清欧美精品xxxxx| 一二三四视频社区在线| 天天夜碰日日摸日日澡性色av| 黄色一级片在线看| www.com毛片| 国产偷人视频免费| 久久黄色片网站| 日本高清xxxx| 日韩a∨精品日韩在线观看| 国产视频九色蝌蚪| av免费观看大全| 99视频免费播放| 一级一片免费播放| 国产精品久久久久7777| 欧美日韩第二页| 色乱码一区二区三区在线| 国产精品波多野结衣| 欧美久久在线观看| 色片在线免费观看| 国产激情在线看| 超碰在线人人爱| 国产91在线亚洲| 欧美黑人又粗又大又爽免费| 五月天色婷婷综合| 国产中文字幕二区| 老司机久久精品| 男女猛烈激情xx00免费视频| 国产一级做a爰片久久| 激情视频小说图片| 亚洲性生活网站| 99久re热视频精品98| aa在线免费观看| www.99riav| 在线免费看污网站| 蜜臀久久99精品久久久酒店新书| 懂色av粉嫩av蜜臀av| 亚洲最大成人在线观看| 妺妺窝人体色777777| 国产大片一区二区三区| 超碰影院在线观看| 男人天堂a在线| 青少年xxxxx性开放hg| 狠狠躁狠狠躁视频专区| 男女猛烈激情xx00免费视频| 波多野结衣网页| 无限资源日本好片| 亚洲人成无码www久久久| 国产精品视频网站在线观看| 91高清国产视频| 黄色三级视频片| 日韩中文字幕三区| 精品少妇人妻av免费久久洗澡| 国产又粗又爽又黄的视频| 三级一区二区三区| 玖玖爱视频在线| 三年中国国语在线播放免费| 无码人妻丰满熟妇区毛片| 免费拍拍拍网站| 国产综合av在线| 成年人视频观看| 欧美日韩在线视频一区二区三区| 国产网站免费在线观看| 久久亚洲中文字幕无码| 99视频在线免费播放| 成人午夜免费在线| 18禁男女爽爽爽午夜网站免费| 干日本少妇首页| 亚洲综合在线网站| 色悠悠久久综合网| av在线网址导航| 日本xxx免费| 999一区二区三区| 日韩av片在线看| 色婷婷狠狠18| 性鲍视频在线观看| 福利在线一区二区| 自慰无码一区二区三区| 国产福利视频在线播放| 九九视频精品在线观看| 精品久久久久av| 国产高清av片| 国内精品视频一区二区三区| 日韩一级免费在线观看| 色乱码一区二区三区在线| 天天在线免费视频| 欧美成人一区二区在线观看| 国产九九在线视频| 免费久久久久久| 亚洲人成无码网站久久99热国产| 激情五月婷婷久久| 黄色污污在线观看| caoporn超碰97| 91麻豆天美传媒在线| 国产91在线免费| 日韩欧美亚洲另类| 免费无码毛片一区二三区| 亚洲 欧美 另类人妖| 久久久久久人妻一区二区三区| 中文字幕第88页| 成人性免费视频| 欧美性受xxxx黑人猛交88| 欧美一区二区三区爽大粗免费| 91丨九色丨蝌蚪| 成年人视频在线免费| 狠狠噜天天噜日日噜| 岛国av在线免费| 免费黄色一级网站| 免费看国产一级片| 经典三级在线视频| 在线视频观看91| 青青在线免费观看视频| 黄色片网址在线观看| 日本一道在线观看| 中文字幕第38页| 91香蕉视频污版| 黄色片一级视频| 欧美性大战久久久久xxx| 国产日本在线播放| 久久久久久久久久久综合| 日本中文字幕二区| 永久免费的av网站| www.亚洲高清| 国产成年人视频网站| 欧美美女一级片| 热久久久久久久久| 999久久久精品视频|