欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解JavaScript的this指向和綁定

 更新時間:2020年09月08日 08:31:32   作者:前端小蜜蜂  
JavaScript 中的 new、bind、call、apply 實際這些都離不開 this,因此本文將著重討論 this,在此過程中分別講解其他相關(guān)知識點。

注意: 本文屬于基礎(chǔ)篇,請大神繞路。如果你不夠了解,或者了解的還不完整,那么可以通過本文來復(fù)習(xí)一下。

this 指向的類型

剛開始學(xué)習(xí) JavaScript 的時候,this 總是最能讓人迷惑,下面我們一起看一下在 JavaScript 中應(yīng)該如何確定 this 的指向。

this 是在函數(shù)被調(diào)用時確定的,它的指向完全取決于函數(shù)調(diào)用的地方,而不是它被聲明的地方(除箭頭函數(shù)外)。當(dāng)一個函數(shù)被調(diào)用時,會創(chuàng)建一個執(zhí)行上下文,它包含函數(shù)在哪里被調(diào)用(調(diào)用棧)、函數(shù)的調(diào)用方式、傳入的參數(shù)等信息,this 就是這個記錄的一個屬性,它會在函數(shù)執(zhí)行的過程中被用到。

this 在函數(shù)的指向有以下幾種場景:

  1. 作為構(gòu)造函數(shù)被 new 調(diào)用
  2. 作為對象的方法使用
  3. 作為函數(shù)直接調(diào)用
  4. 被 call、apply、bind 調(diào)用
  5. 箭頭函數(shù)中的 this

下面我們分別來討論一下這些場景中 this 的指向。

1.new 綁定

函數(shù)如果作為構(gòu)造函數(shù)使用 new 調(diào)用時, this 綁定的是新創(chuàng)建的構(gòu)造函數(shù)的實例。

function Foo() {
 console.log(this)
}

var bar = new Foo() // 輸出: Foo 實例,this 就是 bar

實際上使用 new 調(diào)用構(gòu)造函數(shù)時,會依次執(zhí)行下面的操作:

  • 創(chuàng)建一個新對象
  • 構(gòu)造函數(shù)的 prototype 被賦值給這個新對象的 __proto__
  • 將新對象賦給當(dāng)前的 this
  • 執(zhí)行構(gòu)造函數(shù)
  • 如果函數(shù)沒有返回其他對象,那么 new 表達式中的函數(shù)調(diào)用會自動返回這個新對象,如果返回的不是對象將被忽略

2.顯式綁定

通過 call、apply、bind 我們可以修改函數(shù)綁定的 this,使其成為我們指定的對象。通過這些方法的第一個參數(shù)我們可以顯式地綁定 this。

function foo(name, price) {
 this.name = name
 this.price = price
}

function Food(category, name, price) {
 foo.call(this, name, price) // call 方式調(diào)用
 // foo.apply(this, [name, price])  // apply 方式調(diào)用
 this.category = category
}

new Food('食品', '漢堡', '5塊錢')

// 瀏覽器中輸出: {name: "漢堡", price: "5塊錢", category: "食品"}

call 和 apply 的區(qū)別是 call 方法接受的是參數(shù)列表,而 apply 方法接受的是一個參數(shù)數(shù)組。

func.call(thisArg, arg1, arg2, ...)    // call 用法
func.apply(thisArg, [arg1, arg2, ...])   // apply 用法

而 bind 方法是設(shè)置 this 為給定的值,并返回一個新的函數(shù),且在調(diào)用新函數(shù)時,將給定參數(shù)列表作為原函數(shù)的參數(shù)序列的前若干項。

func.bind(thisArg[, arg1[, arg2[, ...]]])  // bind 用法

舉個例子:

var food = {
 name: '漢堡',
 price: '5塊錢',
 getPrice: function (place) {
  console.log(place + this.price)
 },
}

food.getPrice('KFC ') // 瀏覽器中輸出: "KFC 5塊錢"

var getPrice1 = food.getPrice.bind({ name: '雞腿', price: '7塊錢' }, '肯打雞 ')
getPrice1() // 瀏覽器中輸出: "肯打雞 7塊錢"

關(guān)于 bind 的原理,我們可以使用 apply 方法自己實現(xiàn)一個 bind 看一下:

// ES5 方式
Function.prototype.bind =
 Function.prototype.bind ||
 function () {
  var self = this
  var rest1 = Array.prototype.slice.call(arguments)
  var context = rest1.shift()
  return function () {
   var rest2 = Array.prototype.slice.call(arguments)
   return self.apply(context, rest1.concat(rest2))
  }
 }

// ES6 方式
Function.prototype.bind =
 Function.prototype.bind ||
 function (...rest1) {
  const self = this
  const context = rest1.shift()
  return function (...rest2) {
   return self.apply(context, [...rest1, ...rest2])
  }
 }

ES6 方式用了一些 ES6 的知識比如 rest 參數(shù)、數(shù)組解構(gòu)。

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

var a = 'hello'

function foo() {
 console.log(this.a)
}

foo.call(null) // 瀏覽器中輸出: "hello"

3.隱式綁定

函數(shù)是否在某個上下文對象中調(diào)用,如果是的話 this 綁定的是那個上下文對象。

var a = 'hello'

var obj = {
 a: 'world',
 foo: function () {
  console.log(this.a)
 },
}

obj.foo() // 瀏覽器中輸出: "world"

上面代碼中,foo 方法是作為對象的屬性調(diào)用的,那么此時 foo 方法執(zhí)行時,this 指向 obj 對象。也就是說,此時 this 指向調(diào)用這個方法的對象,如果嵌套了多個對象,那么指向最后一個調(diào)用這個方法的對象:

var a = 'hello'

var obj = {
 a: 'world',
 b: {
  a: 'China',
  foo: function () {
   console.log(this.a)
  },
 },
}

obj.b.foo() // 瀏覽器中輸出: "China"

最后一個對象是 obj 上的 b,那么此時 foo 方法執(zhí)行時,其中的 this 指向的就是 b 對象。

4.默認(rèn)綁定

函數(shù)獨立調(diào)用,直接使用不帶任何修飾的函數(shù)引用進行調(diào)用,也是上面幾種綁定途徑之外的方式。非嚴(yán)格模式下 this 綁定到全局對象(瀏覽器下是 winodw,node 環(huán)境是 global),嚴(yán)格模式下 this 綁定到 undefined (因為嚴(yán)格模式不允許 this 指向全局對象)。

var a = 'hello'

function foo() {
 var a = 'world'
 console.log(this.a)
 console.log(this)
}

foo() // 相當(dāng)于執(zhí)行 window.foo()

// 瀏覽器中輸出: "hello"
// 瀏覽器中輸出: Window 對象

上面代碼中,變量 a 被聲明在全局作用域,成為全局對象 window 的一個同名屬性。函數(shù) foo 被執(zhí)行時,this 此時指向的是全局對象,因此打印出來的 a 是全局對象的屬性。

注意有一種情況:

var a = 'hello'

var obj = {
 a: 'world',
 foo: function () {
  console.log(this.a)
 },
}

var bar = obj.foo

bar() // 瀏覽器中輸出: "hello"

此時 bar 函數(shù),也就是 obj 上的 foo 方法為什么又指向了全局對象呢,是因為 bar 方法此時是作為函數(shù)獨立調(diào)用的,所以此時的場景屬于默認(rèn)綁定,而不是隱式綁定。這種情況和把方法作為回調(diào)函數(shù)的場景類似:

var a = 'hello'

var obj = {
 a: 'world',
 foo: function () {
  console.log(this.a)
 },
}

function func(fn) {
 fn()
}

func(obj.foo) // 瀏覽器中輸出: "hello"

參數(shù)傳遞實際上也是一種隱式的賦值,只不過這里 obj.foo 方法是被隱式賦值給了函數(shù) func 的形參 fn,而之前的情景是自己賦值,兩種情景實際上類似。這種場景我們遇到的比較多的是 setTimeout 和 setInterval,如果回調(diào)函數(shù)不是箭頭函數(shù),那么其中的 this 指向的就是全局對象.

其實我們可以把默認(rèn)綁定當(dāng)作是隱式綁定的特殊情況,比如上面的 bar(),我們可以當(dāng)作是使用 window.bar() 的方式調(diào)用的,此時 bar 中的 this 根據(jù)隱式綁定的情景指向的就是 window。

this 綁定的優(yōu)先級

this 存在多個使用場景,那么多個場景同時出現(xiàn)的時候,this 到底應(yīng)該如何指向呢。這里存在一個優(yōu)先級的概念,this 根據(jù)優(yōu)先級來確定指向。優(yōu)先級:new 綁定 > 顯示綁定 > 隱式綁定 > 默認(rèn)綁定

所以 this 的判斷順序:

  • new 綁定: 函數(shù)是否在 new 中調(diào)用?如果是的話 this 綁定的是新創(chuàng)建的對象;
  • 顯式綁定: 函數(shù)是否是通過 bind、call、apply 調(diào)用?如果是的話,this 綁定的是指定的對象;
  • 隱式綁定: 函數(shù)是否在某個上下文對象中調(diào)用?如果是的話,this 綁定的是那個上下文對象;
  • 如果都不是的話,使用默認(rèn)綁定。如果在嚴(yán)格模式下,就綁定到 undefined,否則綁定到全局對象。

箭頭函數(shù)中的 this

箭頭函數(shù) 是根據(jù)其聲明的地方來決定 this 的,它是 ES6 的知識點。

箭頭函數(shù)的 this 綁定是無法通過 call、apply、bind 被修改的,且因為箭頭函數(shù)沒有構(gòu)造函數(shù) constructor,所以也不可以使用 new 調(diào)用,即不能作為構(gòu)造函數(shù),否則會報錯。

var a = 'hello'

var obj = {
 a: 'world',
 foo: () => {
  console.log(this.a)
 },
}

obj.foo() // 瀏覽器中輸出: "hello"

我們可以看看 ECMAScript 標(biāo)準(zhǔn)中對箭頭函數(shù)的描述。

原文:

An Arrow Function does not define local bindings for arguments, super, this, or new.target. Any reference to arguments, super, this, or new.target within an ArrowFunction must resolve to a binding in a lexically enclosing environment. Typically this will be the Function Environment of an immediately enclosing function.

翻譯:

箭頭函數(shù)不為 arguments、super、this 或 new.target 定義本地綁定。箭頭函數(shù)中對 arguments、super、this 或 new.target 的任何引用都解析為當(dāng)前所在詞法作用域中的綁定。通常,這是箭頭函數(shù)所在的函數(shù)作用域。

— ECMAScript Language Specification - Arrow Function | ECMA 標(biāo)準(zhǔn) - 箭頭函數(shù)

一個 this 的小練習(xí)

用一個小練習(xí)來實戰(zhàn)一下:

var a = 20

var obj = {
 a: 40,
 foo: () => {
  console.log(this.a)

  function func() {
   this.a = 60
   console.log(this.a)
  }

  func.prototype.a = 50
  return func
 },
}

var bar = obj.foo() // 瀏覽器中輸出: 20
bar() // 瀏覽器中輸出: 60
new bar() // 瀏覽器中輸出: 60

稍微解釋一下:

1)var a = 20 這句在全局變量 window 上創(chuàng)建了個屬性 a 并賦值為 20;

2)首先執(zhí)行的是 obj.foo(),這是一個箭頭函數(shù),箭頭函數(shù)不創(chuàng)建新的函數(shù)作用域直接沿用語句外部的作用域,因此 obj.foo() 執(zhí)行時箭頭函數(shù)中 this 是全局 window,首先打印出 window 上的屬性 a 的值 20,箭頭函數(shù)返回了一個原型上有個值為 50 的屬性 a 的函數(shù)對象 func 給 bar;

3)繼續(xù)執(zhí)行的是 bar(),這里執(zhí)行的是剛剛箭頭函數(shù)返回的閉包 func,其內(nèi)部的 this 指向 window,因此 this.a 修改了 window.a 的值為 60 并打印出來;

4)然后執(zhí)行的是 new bar(),根據(jù)之前的表述,new 操作符會在 func 函數(shù)中創(chuàng)建一個繼承了 func 原型的實例對象并用 this 指向它,隨后 this.a = 60 又在實例對象上創(chuàng)建了一個屬性 a,在之后的打印中已經(jīng)在實例上找到了屬性 a,因此就不繼續(xù)往對象原型上查找了,所以打印出第三個 60。

如果把上面例子的箭頭函數(shù)換成普通函數(shù)呢,結(jié)果會是什么樣?

var a = 20

var obj = {
 a: 40,
 foo: function () {
  console.log(this.a)

  function func() {
   this.a = 60
   console.log(this.a)
  }

  func.prototype.a = 50
  return func
 },
}

var bar = obj.foo() // 瀏覽器中輸出: 40
bar() // 瀏覽器中輸出: 60
new bar() // 瀏覽器中輸出: 60

這個例子就不詳細(xì)講解了。

如果把上面兩個例子弄懂原理,基本上 this 的指向就掌握的差不多啦~

以上就是詳解JavaScript的this指向和綁定的詳細(xì)內(nèi)容,更多關(guān)于JavaScript的this指向和綁定的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 詳解小程序開發(fā)經(jīng)驗:多頁面數(shù)據(jù)同步

    詳解小程序開發(fā)經(jīng)驗:多頁面數(shù)據(jù)同步

    這篇文章主要介紹了小程序開發(fā)經(jīng)驗:多頁面數(shù)據(jù)同步,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-05-05
  • 詳解CocosCreator中幾種計時器的使用方法

    詳解CocosCreator中幾種計時器的使用方法

    這篇文章主要介紹了CocosCreator中幾種計時器的使用方法,推薦使用schedule,功能多些,銷毀時還能自動移除
    2021-04-04
  • 小程序瀑布流組件實現(xiàn)翻頁與圖片懶加載

    小程序瀑布流組件實現(xiàn)翻頁與圖片懶加載

    這篇文章主要介紹了小程序瀑布流組件實現(xiàn)翻頁與圖片懶加載,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-05-05
  • 在微信小程序中保存網(wǎng)絡(luò)圖片

    在微信小程序中保存網(wǎng)絡(luò)圖片

    這篇文章主要介紹了在微信小程序中保存網(wǎng)絡(luò)圖片,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-02-02
  • JavaScript實現(xiàn)班級抽簽小程序

    JavaScript實現(xiàn)班級抽簽小程序

    這篇文章主要為大家詳細(xì)介紹了JavaScript實現(xiàn)班級抽簽小程序,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-05-05
  • javascript版2048小游戲

    javascript版2048小游戲

    本文給大家分享的是使用javascript制作的2048小游戲的代碼,僅僅是想鍛煉下自己的編程代碼風(fēng)格,盡量做的規(guī)范些,小伙伴們多多給些建議。
    2015-03-03
  • 微信小程序 多行文本顯示...+顯示更多按鈕和收起更多按鈕功能

    微信小程序 多行文本顯示...+顯示更多按鈕和收起更多按鈕功能

    這篇文章主要介紹了微信小程序多行文本顯示...+顯示更多按鈕和收起更多按鈕,代碼簡單易懂,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-09-09
  • JavaScript實現(xiàn)網(wǎng)頁跨年倒計時

    JavaScript實現(xiàn)網(wǎng)頁跨年倒計時

    這篇文章主要為大家詳細(xì)介紹了JavaScript實現(xiàn)網(wǎng)頁跨年倒計時,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-12-12
  • JS使用new操作符創(chuàng)建對象的方法分析

    JS使用new操作符創(chuàng)建對象的方法分析

    這篇文章主要介紹了JS使用new操作符創(chuàng)建對象的方法,結(jié)合實例形式分析了javascript面向?qū)ο蟪绦蛟O(shè)計類的定義、new操作符對象的創(chuàng)建及相關(guān)操作注意事項,需要的朋友可以參考下
    2019-05-05
  • JS表單提交中onsubmit事件return的作用詳解

    JS表單提交中onsubmit事件return的作用詳解

    這篇文章主要為大家介紹了JS表單提交中onsubmit事件return的作用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-10-10

最新評論