溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

JavaScript?this指向綁定方式及不適用情況是什么

發(fā)布時間:2023-04-11 10:32:46 來源:億速云 閱讀:84 作者:iii 欄目:開發(fā)技術(shù)

這篇文章主要講解了“JavaScript this指向綁定方式及不適用情況是什么”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“JavaScript this指向綁定方式及不適用情況是什么”吧!

問題復(fù)現(xiàn)

最近在研究函數(shù)防抖場景時看到如下代碼:

function debounce(fn, delay) {
  var timer; // 維護(hù)一個 timer
  return function () {
    var _this = this; // 取 debounce 執(zhí)行作用域的 this
    var args = arguments;
    if (timer) {
      clearTimeout(timer);
    }
    timer = setTimeout(function () {
      fn.apply(_this, args); // 用 apply 指向調(diào)用 debounce 的對象,相當(dāng)于 _this.fn(args);
    }, delay);
  };
}

其中有一段代碼是 var _this = this 這段代碼出現(xiàn)在由 return 返回的匿名函數(shù)中,這個時候我就有些懵逼了,因為根據(jù)我匱乏的 js 知識,這里的 this 應(yīng)該是指向全局作用域才對,為什么能像注釋那樣指向 debounce 執(zhí)行時的作用域呢?感覺如下所寫是否更加合理呢?(事實證明這么寫肯定是不對的)

function debounce(fn, delay) {
    var timer
    var _this = this
    return function() {
    ...
    }
}

于是我打算用代碼來實測這里的 debounce 執(zhí)行作用域中的 this 到底指的是什么,它會變化嗎?還是根據(jù)我的理解只要是像這樣類似的匿名函數(shù),其中的 this 都是指向全局的呢? 于是我寫下如下代碼(關(guān)鍵部分):

body 部分新增一個 button 標(biāo)簽

<button>我是button</button>

script 標(biāo)簽內(nèi)部代碼如下:

//函數(shù)防抖
function debounce(fn, delay) {
  var timer; // 維護(hù)一個 timer
  return function () {
    var _this = this; // 取 debounce 執(zhí)行作用域的 this
    var args = arguments;
    if (timer) {
      clearTimeout(timer);
    }
    timer = setTimeout(function () {
      fn.apply(_this, args); // 用 apply 指向調(diào)用 debounce 的對象,相當(dāng)于 _this.fn(args);
    }, delay);
  };
}
var btn = document.getElementsByTagName('button')[0]
btn.onclick= debounce(function() {
  console.log(this)
}, 1000)

點(diǎn)擊按鈕,看看控制臺輸出 this 到底是誰,按照我之前的理解輸出的 this 應(yīng)該是window 全局對象才對

<button>我是button</button>

出乎意料,這里的 this 輸出的是 button 元素,于是我再在上述腳本中新增一個事件綁定:

window.onclick = debounce(function() {
 &nbsp;console.log(this)
}, 1000)

點(diǎn)擊頁面空白處輸入如下:

JavaScript?this指向綁定方式及不適用情況是什么

這次輸出的就是 window 了! 看來這里的 this 實際是跟 debounce 函數(shù)所返回函數(shù)的實際調(diào)用者有關(guān),第一次控制臺輸出的是 button 元素,因為是通過 button 元素來調(diào)用該返回函數(shù),第二次調(diào)用者就是 widnow,舉這段

btn.onclick = dobounce(function() {console.log(this)}, 1000)

代碼的例子:

  • 頁面初始化完畢后,執(zhí)行腳本代碼,debounce 函數(shù)接收一個具體函數(shù)(將其命名為 fn 好了)和一個時間間隔參數(shù)( intervcal )

  • 進(jìn)到 debounce 代碼內(nèi)部,return 一個匿名函數(shù),并賦給 btn.onclick,實際上就是事件綁定

  • 所以說當(dāng)我點(diǎn)擊 button 的時候,btn.onclick 的執(zhí)行代碼是這樣的:

btn.onclick = function() {
    var _this = this; // 取 debounce 執(zhí)行作用域的 this
    var args = arguments;
    if (timer) {
        clearTimeout(timer);
    }
    //因為閉包的存在 timer 還是取的 debounce 中的 timer
    timer = setTimeout(function () {
        fn.apply(_this, args); // 用 apply 指向調(diào)用 debounce 的對象,相當(dāng)于 _this.fn(args);
    }, delay);
}

那么這里的 this 指向的就是 button 元素了,為什么呢,以上的例子引出我們今天的主題 - 函數(shù)的 this 指向

調(diào)用位置

關(guān)于函數(shù)的 this ,常常有句話,叫做誰調(diào)用就指向誰。簡單來說 this 的指向跟函數(shù)的調(diào)用位置緊密相關(guān),要想知道函數(shù)調(diào)用時 this 到底引用了什么,就應(yīng)該明確函數(shù)的調(diào)用位置。一般來說需要通過函數(shù)的調(diào)用棧來判斷來分析出函數(shù)真正的調(diào)用位置,具體怎么分析呢?除了目測代碼外,還也可以借用瀏覽器的開發(fā)者工具( debug 工具),去推斷目標(biāo)函數(shù)到底是在哪里調(diào)用的,這樣才能更準(zhǔn)確的知曉this的指向。比如下面這段代碼:

function foo() {
  console.log('foo')
}
function bar() {
  console.log('bar')
  foo()
}
bar()

要想知道 foo 函數(shù)是由誰調(diào)用的,就可以在瀏覽器中打開調(diào)試工具,在 foo 函數(shù)中的第一行打一個斷點(diǎn),找到函數(shù)的調(diào)用棧,然后再找到棧中的第二個元素,這就是真正的調(diào)用位置。如下圖所示:

JavaScript?this指向綁定方式及不適用情況是什么

從瀏覽器的調(diào)試工具可以找到 foo 函數(shù)的真正調(diào)用位置。

默認(rèn)綁定

var a = 2
function foo() {
  var a = 3
  console.log(this.a)
}
foo() // 2

輸出結(jié)果為 2。因為 foo 函數(shù)調(diào)用時處于全局環(huán)境下(這里是 window ),查看一下瀏覽器中的調(diào)用棧:

JavaScript?this指向綁定方式及不適用情況是什么

調(diào)用棧中只有 foo 函數(shù)一個元素,說明調(diào)用者就是當(dāng)前的全局環(huán)境 window ,所以這里的 this 指向的就是 window,因為最外部的 a 一開始是最為 window.a 聲明并賦值的,所以可以理解為this = window; this.a = 2。比較特殊的一點(diǎn)就是,如果在 foo 函數(shù)內(nèi)部采用了嚴(yán)格模式,那么 this 就會綁定到 undefined:

var a = 2
function foo() {
  'use strict'
  var a = 3
  console.log(this.a)
}
foo() //`//Cannot read property 'a' of undefined`

隱式綁定

舉如下代碼為例:

var a = 2
function foo() {
  console.log(this.a)
}
var obj1 = {
  a:3,
  foo: foo
}
obj.foo() //3

輸出結(jié)果為 3,說明這里的 this 指向的是 obj1,為什么不再是指向全局環(huán)境了呢。在這里就要考慮到調(diào)用位置是否存在上下文對象,或者說是否被某個對象擁有或包含。在上述的代碼中,foo 函數(shù)的引用被賦給了 obj1 的 foo 屬性obj1.foo = foo, 并且在 foo 函數(shù)被調(diào)用時,它的前面也加上了對 obj1 的引用。此時,當(dāng)函數(shù)引用有上下文對象時,隱式綁定規(guī)則就會將函數(shù)中的this綁定到這個上下文對象,這里的上下文對象就是 obj1。 其實在理解上下文對象時,個人覺得不用那么抽象,它無非就是一個不確定的代名詞,簡單來說你覺得它是什么,那它就是什么。

顯式綁定

默認(rèn)綁定和隱式綁定在我看來是 js 的一個內(nèi)置且被動的綁定方式,就是已經(jīng)這么幫你設(shè)定好了,只要符合這兩個規(guī)則且沒有其他規(guī)則存在那么 this 的指向就按照這兩個規(guī)則來。顯然,這類被動的綁定方式并不符合實際的代碼編寫需要,比如我要指定一個函數(shù)的 this ,該怎么辦呢?這時候就需要顯式綁定了。call、apply 會在顯式綁定時發(fā)揮作用。參考如下代碼:

function foo() {
  console.log(this.a)
}
var obj1 = {
  a: 2
}
var a = 3
foo.call(obj1) // 2

輸出結(jié)果為 2。原因是因為 call 方法改變了 foo 函數(shù)運(yùn)行的 this 指向,將原本 this 指向的 window 全局轉(zhuǎn)為了指向 obj1 ,所以輸出的是 2,從這里也可以看出,顯示綁定的優(yōu)先級大于默認(rèn)綁定。

new 綁定

首先應(yīng)該明確一點(diǎn),JavaScript 中的 new 與其他面向類的語言不同,在 js 中 new 后面的只不過是一個普通的函數(shù),僅僅是被 new 操作符調(diào)用了而已。使用 new 調(diào)用函數(shù)時,會執(zhí)行如下步驟:

  • 創(chuàng)建(或者說構(gòu)造)一個全新的對象。

  • 這個新對象會被執(zhí)行 [[Prototype]] 連接。

  • 這個新對象會綁定到函數(shù)調(diào)用的 this。

  • 如果函數(shù)沒有返回其他對象,那么 new 表達(dá)式中的函數(shù)調(diào)用會自動返回這個新對象。 代碼如下所示:

function foo(a) {
  this.a = a
}
var bar = new foo(2)
console.log(bar.a) // 2

輸出結(jié)果為 2。

不適用的情況

ES6 中出現(xiàn)了一種特殊的函數(shù):箭頭函數(shù)。以上的四種規(guī)則在箭頭函數(shù)中都不適用,箭頭函數(shù)的是根據(jù)外層函數(shù)或者全局鏈決定 this 的。其實這也是對以往 ES6 之前的較為復(fù)雜的 this 綁定規(guī)則的優(yōu)化和統(tǒng)一,在實際編碼的過程中更容易讓人理解,當(dāng)然箭頭函數(shù)也有缺點(diǎn),這里就不再展開。

感謝各位的閱讀,以上就是“JavaScript this指向綁定方式及不適用情況是什么”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對JavaScript this指向綁定方式及不適用情況是什么這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關(guān)知識點(diǎn)的文章,歡迎關(guān)注!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI