您好,登錄后才能下訂單哦!
這篇文章主要講解了“JavaScript this指向綁定方式及不適用情況是什么”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“JavaScript this指向綁定方式及不適用情況是什么”吧!
最近在研究函數防抖場景時看到如下代碼:
function debounce(fn, delay) { var timer; // 維護一個 timer return function () { var _this = this; // 取 debounce 執行作用域的 this var args = arguments; if (timer) { clearTimeout(timer); } timer = setTimeout(function () { fn.apply(_this, args); // 用 apply 指向調用 debounce 的對象,相當于 _this.fn(args); }, delay); }; }
其中有一段代碼是 var _this = this
這段代碼出現在由 return 返回的匿名函數中,這個時候我就有些懵逼了,因為根據我匱乏的 js 知識,這里的 this 應該是指向全局作用域才對,為什么能像注釋那樣指向 debounce 執行時的作用域呢?感覺如下所寫是否更加合理呢?(事實證明這么寫肯定是不對的)
function debounce(fn, delay) { var timer var _this = this return function() { ... } }
于是我打算用代碼來實測這里的 debounce 執行作用域中的 this 到底指的是什么,它會變化嗎?還是根據我的理解只要是像這樣類似的匿名函數,其中的 this 都是指向全局的呢? 于是我寫下如下代碼(關鍵部分):
body 部分新增一個 button 標簽
<button>我是button</button>
script 標簽內部代碼如下:
//函數防抖 function debounce(fn, delay) { var timer; // 維護一個 timer return function () { var _this = this; // 取 debounce 執行作用域的 this var args = arguments; if (timer) { clearTimeout(timer); } timer = setTimeout(function () { fn.apply(_this, args); // 用 apply 指向調用 debounce 的對象,相當于 _this.fn(args); }, delay); }; } var btn = document.getElementsByTagName('button')[0] btn.onclick= debounce(function() { console.log(this) }, 1000)
點擊按鈕,看看控制臺輸出 this 到底是誰,按照我之前的理解輸出的 this 應該是window 全局對象才對
<button>我是button</button>
出乎意料,這里的 this 輸出的是 button 元素,于是我再在上述腳本中新增一個事件綁定:
window.onclick = debounce(function() { console.log(this) }, 1000)
點擊頁面空白處輸入如下:
這次輸出的就是 window 了! 看來這里的 this 實際是跟 debounce 函數所返回函數的實際調用者有關,第一次控制臺輸出的是 button 元素,因為是通過 button 元素來調用該返回函數,第二次調用者就是 widnow,舉這段
btn.onclick = dobounce(function() {console.log(this)}, 1000)
代碼的例子:
頁面初始化完畢后,執行腳本代碼,debounce 函數接收一個具體函數(將其命名為 fn 好了)和一個時間間隔參數( intervcal )
進到 debounce 代碼內部,return 一個匿名函數,并賦給 btn.onclick,實際上就是事件綁定
所以說當我點擊 button 的時候,btn.onclick 的執行代碼是這樣的:
btn.onclick = function() { var _this = this; // 取 debounce 執行作用域的 this var args = arguments; if (timer) { clearTimeout(timer); } //因為閉包的存在 timer 還是取的 debounce 中的 timer timer = setTimeout(function () { fn.apply(_this, args); // 用 apply 指向調用 debounce 的對象,相當于 _this.fn(args); }, delay); }
那么這里的 this 指向的就是 button 元素了,為什么呢,以上的例子引出我們今天的主題 - 函數的 this 指向
關于函數的 this ,常常有句話,叫做誰調用就指向誰
。簡單來說 this 的指向跟函數的調用位置緊密相關,要想知道函數調用時 this 到底引用了什么,就應該明確函數的調用位置。一般來說需要通過函數的調用棧來判斷來分析出函數真正的調用位置,具體怎么分析呢?除了目測代碼外,還也可以借用瀏覽器的開發者工具( debug 工具),去推斷目標函數到底是在哪里調用的,這樣才能更準確的知曉this的指向。比如下面這段代碼:
function foo() { console.log('foo') } function bar() { console.log('bar') foo() } bar()
要想知道 foo 函數是由誰調用的,就可以在瀏覽器中打開調試工具,在 foo 函數中的第一行打一個斷點,找到函數的調用棧,然后再找到棧中的第二個元素,這就是真正的調用位置。如下圖所示:
從瀏覽器的調試工具可以找到 foo 函數的真正調用位置。
var a = 2 function foo() { var a = 3 console.log(this.a) } foo() // 2
輸出結果為 2。因為 foo 函數調用時處于全局環境下(這里是 window ),查看一下瀏覽器中的調用棧:
調用棧中只有 foo 函數一個元素,說明調用者就是當前的全局環境 window ,所以這里的 this 指向的就是 window,因為最外部的 a 一開始是最為 window.a 聲明并賦值的,所以可以理解為this = window; this.a = 2
。比較特殊的一點就是,如果在 foo 函數內部采用了嚴格模式,那么 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
輸出結果為 3,說明這里的 this 指向的是 obj1,為什么不再是指向全局環境了呢。在這里就要考慮到調用位置是否存在上下文對象,或者說是否被某個對象擁有或包含。在上述的代碼中,foo 函數的引用被賦給了 obj1 的 foo 屬性obj1.foo = foo
, 并且在 foo 函數被調用時,它的前面也加上了對 obj1 的引用。此時,當函數引用有上下文對象時,隱式綁定規則就會將函數中的this綁定到這個上下文對象,這里的上下文對象就是 obj1。 其實在理解上下文對象時,個人覺得不用那么抽象,它無非就是一個不確定的代名詞,簡單來說你覺得它是什么,那它就是什么。
默認綁定和隱式綁定在我看來是 js 的一個內置且被動的綁定方式,就是已經這么幫你設定好了,只要符合這兩個規則且沒有其他規則存在那么 this 的指向就按照這兩個規則來。顯然,這類被動的綁定方式并不符合實際的代碼編寫需要,比如我要指定一個函數的 this ,該怎么辦呢?這時候就需要顯式綁定了。call、apply 會在顯式綁定時發揮作用。參考如下代碼:
function foo() { console.log(this.a) } var obj1 = { a: 2 } var a = 3 foo.call(obj1) // 2
輸出結果為 2。原因是因為 call 方法改變了 foo 函數運行的 this 指向,將原本 this 指向的 window 全局轉為了指向 obj1 ,所以輸出的是 2,從這里也可以看出,顯示綁定的優先級大于默認綁定。
首先應該明確一點,JavaScript 中的 new 與其他面向類的語言不同,在 js 中 new 后面的只不過是一個普通的函數,僅僅是被 new 操作符調用了而已。使用 new 調用函數時,會執行如下步驟:
創建(或者說構造)一個全新的對象。
這個新對象會被執行 [[Prototype]] 連接。
這個新對象會綁定到函數調用的 this。
如果函數沒有返回其他對象,那么 new 表達式中的函數調用會自動返回這個新對象。 代碼如下所示:
function foo(a) { this.a = a } var bar = new foo(2) console.log(bar.a) // 2
輸出結果為 2。
ES6 中出現了一種特殊的函數:箭頭函數。以上的四種規則在箭頭函數中都不適用,箭頭函數的是根據外層函數或者全局鏈決定 this 的。其實這也是對以往 ES6 之前的較為復雜的 this 綁定規則的優化和統一,在實際編碼的過程中更容易讓人理解,當然箭頭函數也有缺點,這里就不再展開。
感謝各位的閱讀,以上就是“JavaScript this指向綁定方式及不適用情況是什么”的內容了,經過本文的學習后,相信大家對JavaScript this指向綁定方式及不適用情況是什么這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。