摘要:關鍵字會實例化一個新的對象實例,并在執行構造函數時將指向該實例。原文鏈接譯是什么對象的內部工作原理
原文鏈接:What is this? The Inner Workings of JavaScript Objects (需要梯子)
原文作者:Eric Elliott
譯文永久鏈接:【譯】什么是 this?JavaScript 對象的內部工作原理
譯者:士心
翻譯目的:函數動態綁定 this 的特性,經常讓開發者感到頭疼,這篇文章能幫助梳理概念
JavaScript 是一種支持面向對象編程和動態綁定的多范式語言。動態綁定是其一個強大的特性,它允許 JavaScript 代碼在運行時更改 this,但是這一強大而且靈活的特性卻會給開發者帶來一些困惑,這些困惑集中在 JavaScript 代碼運行時的表現上。
動態綁定(Dynamic Binding)動態綁定指的是在運行時才確定調用函數的方式,而不是更早的編譯階段。JavaScript 通過 this 和原型鏈來體現動態綁定。也就是說,函數內部的 this 是在運行時確定,并且定義函數的方式不同,確定 this 的規則也不同。
先來玩個游戲。這個游戲叫 “this 是什么?”
const a = { a: "a" }; const obj = { getThis: () => this, getThis2 () { return this; } }; obj.getThis3 = obj.getThis.bind(obj); obj.getThis4 = obj.getThis2.bind(obj); const answers = [ obj.getThis(), obj.getThis.call(a), obj.getThis2(), obj.getThis2.call(a), obj.getThis3(), obj.getThis3.call(a), obj.getThis4(), obj.getThis4.call(a) ];
在繼續之前,請先寫下你的答案。完成后,console.log()你的答案,你答對了嗎?
譯者注:本文的示例需要運行在 ES6 Module 下才符合文章所說的結果,你可以在 HTML 頁面中指定腳本標簽類型... 讓代碼運行在 ES6 Module 下。如果你將代碼直接復制到瀏覽器控制臺運行,下文說的 undefined 實際上是 window。
讓我們從第一個結果開始。obj.getThis() 返回 undefined,但為什么呢?箭頭函數永遠不會綁定屬于自己的 this,它們的 this 總是綁定在定義時所在的作用域上。本例中的定義時所在的作用域,就是 ES6 模塊的根作用域,這里的 this 是 undefined。因為相同的原因,obj.getThis.call(a) 同樣也是 undefined。對于箭頭函數,它的 this 不能被重新分配賦值,即使使用 .call() 或 .bind(),它的 this 總是綁定在定義時所在的作用域上,而不會指向運行時所在的作用域。
obj.getThis2() 通過一般的函數調用獲取其綁定。如果函數之前沒有綁定 this(就是說,它不是箭頭函數),那該函數就可以擁有自己的 this 綁定,具體綁定到使用 . 或 [] 調用該方法的對象上。
obj.getThis2.call(a) 做了點小動作,call() 方法提供給定的 this 值和可選參數調用函數。換句話說,函數從 .call() 參數獲取 this 的綁定,因此 obj.getThis2.call(a) 返回 a 對象。
使用 obj.getThis3 = obj.getThis.bind(obj);,我們嘗試綁定一個箭頭函數,前面我們已經討論過綁定箭頭函數是不起作用的,所以 obj.getThis3() 和 obj.getThis3.call(a) 都得到 undefined。
我們可以綁定一般的函數,所以 obj.getThis4() 按預期返回 obj,因為它已經使用 obj.getThis4 = obj.getThis2.bind(obj); 綁定了。而 obj.getThis4.call(a) 遵從第一個的綁定,所以返回 obj 而不是 a。
加大難度(Curve Ball)同樣的挑戰,不過這一次,使用到了 class 的公共字段語法(public fields syntax) (寫這篇文章的時候,該語法提案處于 Stage3 階段。Chrome 和 @babel/plugin-proposal-class-properties 已經支持):
譯者注:公共字段語法(public fields syntax),如果不知道是什么的話,可以看阮一峰 ES6 | Class 的基本語法: 實例屬性的新寫法
class Obj { getThis = () => this getThis2 () { return this; } } const obj2 = new Obj(); obj2.getThis3 = obj2.getThis.bind(obj2); obj2.getThis4 = obj2.getThis2.bind(obj2); const answers2 = [ obj2.getThis(), obj2.getThis.call(a), obj2.getThis2(), obj2.getThis2.call(a), obj2.getThis3(), obj2.getThis3.call(a), obj2.getThis4(), obj2.getThis4.call(a) ];
在繼續之前寫下你的答案。
準備好了?
除了 obj2.getThis2.call(a) 返回 a 對象外,其它都返回對象實例。箭頭函數的 this 仍然綁定在定義時所在的作用域上,區別在于定義時所在作用域的 this 已然不是 undefined。這段代碼的底層,會將類的屬性賦值編譯成:
class Obj { constructor() { this.getThis = () => this; } ...
也就是說,箭頭函數是在構造函數(constructor)的上下文中定義的。由于它是一個類,創建實例的唯一方法是使用 new 關鍵字(省略 new 會拋出錯誤)。
new 關鍵字會實例化一個新的對象實例,并在執行構造函數時將 this 指向該實例。這種行為,加上我們上面已經提到的其他行為,就能解釋清楚結果。
總結你做得怎樣?有沒有做對呢?理解了 this 的表現行為,在調試棘手的問題時能節省大量時間。如果你做錯了任意一道,那你需要多加練習。研究上面這些例子,然后回來再做一次,直到你都能做對,并向其他人解釋為什么這些方法會返回這些值。
如果這些題比你想象的要難,你并不是一個人。針對這個主題,我已經問過了不少開發者,我認為到目前為止只有一位開發人員掌握了。
加上類和箭頭函數的行為,會使 .call(),. bind() 或 .apply() 的動態綁定開始變得復雜。請記住,箭頭函數總是將 this 綁定在定義時所在的作用域上,第二個例子 class 中的 this 實際綁定在執行構造函數時的作用域上。如果你還有疑問,請記住使用 debugger 工具來驗證是否符合你的想法。
還要記住,在JavaScript中,即使不用 this,你也可以做很多事情。根據我的經驗,幾乎任何東西都可以使用純函數重新實現,純函數接收所有傳遞給它們的顯式參數(你可以將 this 看成是可變的隱式參數)。封裝在純函數中的邏輯具有確定性,這使得它更易于測試,并且沒有副作用。這意味著與操作 this 不同,你不可能破壞其他任何東西。而每當你修改 this 時,依賴于 this 的行為就可能被破壞。
也就是說,this 有時很有用,例如:在大量對象之間共享方法。即使在函數式編程中,this 對于訪問其他對象上的函數,以實現在現有函數之上構建新函數是很有用的,例如:.flatMap() 可以通過組合 this.map() 和 this.constructor.of() 來實現。
如果你喜歡這篇文章,請關注我,我會持續輸出更多原創且高質量的內容。
原文鏈接:【譯】this 是什么?JavaScript 對象的內部工作原理
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/103801.html
摘要:主題來自于的典型面試問題列表。有多種方法來處理事件委托。這種方法的缺點是父容器的偵聽器可能需要檢查事件來選擇正確的操作,而元素本身不會是一個監聽器。 showImg(http://fw008950-flywheel.netdna-ssl.com/wp-content/uploads/2014/11/Get-Hired-Fast-How-to-Job-Search-Classifieds...
摘要:本章將會深入谷歌引擎的內部結構。一個引擎可以用標準解釋程序或者即時編譯器來實現,即時編譯器即以某種形式把解釋為字節碼。引擎的由來引擎是由谷歌開源并以語言編寫。注意到沒有使用中間字節碼來表示,這樣就不需要解釋器了。 原文請查閱這里,略有刪減。 本系列持續更新中,Github 地址請查閱這里。 這是 JavaScript 工作原理的第二章。 本章將會深入谷歌 V8 引擎的內部結構。我們也會...
摘要:是如何工作的內存管理以及如何處理四種常見的內存泄漏原文譯者幾個禮拜之前我們開始一系列對于以及其本質工作原理的深入挖掘我們認為通過了解的構建方式以及它們是如何共同合作的,你就能夠寫出更好的代碼以及應用。 JavaScript是如何工作的:內存管理以及如何處理四種常見的內存泄漏 原文:How JavaScript works: memory management + how to han...
摘要:當面試中讓我解釋一下閉包時我懵逼了。這個解釋開始可能有點晦澀,讓我們抽絲剝繭摘下閉包的真面目。此文不詳述作用域有專門的主題闡述,不過作用域是理解閉包原理的基礎。這才是閉包的真正便利之處。閉包使用不當就會很坑。 原文鏈接 為什么深度學習JavaScript? JavaScript如今是最流行的編程語言之一。它運行在瀏覽器、服務器、移動設備、桌面應用,也可能包括冰箱。無需我舉其他再多不相干...
摘要:本章會對語言引擎,運行時,調用棧做一個概述。調用棧只是一個單線程的編程語言,這意味著它只有一個調用棧。查看如下代碼當引擎開始執行這段代碼的時候,調用棧會被清空。之后,產生如下步驟調用棧中的每個入口被稱為堆棧結構。 原文請查閱這里,本文采用知識共享署名 4.0 國際許可協議共享,BY Troland。 本系列持續更新中,Github 地址請查閱這里。 這是 JavaScript 工作原...
閱讀 2033·2023-04-25 14:50
閱讀 2912·2021-11-17 09:33
閱讀 2616·2019-08-30 13:07
閱讀 2844·2019-08-29 16:57
閱讀 913·2019-08-29 15:26
閱讀 3550·2019-08-29 13:08
閱讀 1996·2019-08-29 12:32
閱讀 3391·2019-08-26 13:57