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