摘要:請(qǐng)注意,就變量生命周期而言,聲明階段與變量聲明是不同的概念。提升在生命周期中無(wú)效的原因如上所述,提升是變量在作用域頂部的耦合聲明和初始化階段。然而,生命周期分離聲明和初始化階段。解耦消除了的提升期限。
為了保證的可讀性,本文采用意譯而非直譯。
提升是將變量或函數(shù)定義移動(dòng)到作用域頭部的過(guò)程,通常是 var 聲明的變量和函數(shù)聲明function fun() {...}。
當(dāng) ES6 引入let(以及與let類似聲明的const和class)聲明時(shí),許多開(kāi)發(fā)人員都使用提升定義來(lái)描述如何訪問(wèn)變量。但是在對(duì)這個(gè)問(wèn)題進(jìn)行了更多的探討之后,令我驚訝的是提升并不是描述let變量的初始化和可用性的正確術(shù)語(yǔ)。
ES6 為let提供了一個(gè)不同的和改進(jìn)的機(jī)制。它要求更嚴(yán)格的變量聲明,在定義之前不能使用,從而提高代碼質(zhì)量。
想閱讀更多優(yōu)質(zhì)文章請(qǐng)猛戳GitHub博客,一年百來(lái)篇優(yōu)質(zhì)文章等著你!
1. 容易出錯(cuò)的 var 提升有時(shí)候我們會(huì)在zuo內(nèi)作用域內(nèi)看到一個(gè)奇怪的變量var varname和函數(shù)函數(shù)function funName() {...} 聲明:
// var hoisting num; // => undefined var num; num = 10; num; // => 10 // function hoisting getPi; // => function getPi() {...} getPi(); // => 3.14 function getPi() { return 3.14; }
變量num在聲明var num之前被訪問(wèn),因此它被賦值為undefined。fucntion getPi(){…}在文件末尾定義。但是,可以在聲明getPi()之前調(diào)用該函數(shù),因?yàn)樗惶嵘阶饔糜虻捻敳俊?/p>
事實(shí)證明,先使用然后聲明變量或函數(shù)的可能性會(huì)造成混淆。假設(shè)您滾動(dòng)一個(gè)大文件,突然看到一個(gè)未聲明的變量,它到底是如何出現(xiàn)在這里的,以及它在哪里定義的?
當(dāng)然,一個(gè)熟練的JavaScript開(kāi)發(fā)人員不會(huì)這樣編寫(xiě)代碼。但是在成千上萬(wàn)的JavaScript中,GitHub repos是很有可能處理這樣的代碼的。
即使查看上面給出的代碼示例,也很難理解代碼中的聲明流。
當(dāng)然,首先要聲明再使用。let 鼓勵(lì)咱們使用這種方法處理變量。
2. 理解背后原理:變量生命周期當(dāng)引擎處理變量時(shí),它們的生命周期由以下階段組成:
聲明階段(Declaration phase)是在作用域中注冊(cè)一個(gè)變量。
初始化階段(Initialization phase)是分配內(nèi)存并為作用域中的變量創(chuàng)建綁定。 在此步驟中,變量將使用undefined自動(dòng)初始化。
賦值階段(Assignment phase)是為初始化的變量賦值。
變量在通過(guò)聲明階段時(shí)尚未初始化狀態(tài),但未達(dá)到初始化狀態(tài)。
請(qǐng)注意,就變量生命周期而言,聲明階段與變量聲明是不同的概念。 簡(jiǎn)而言之,JS引擎在3個(gè)階段處理變量聲明:聲明階段,初始化階段和賦值階段。
3.var 變量的生命周期熟悉生命周期階段之后,讓我們使用它們來(lái)描述JS引擎如何處理var變量。
假設(shè)JS遇到一個(gè)函數(shù)作用域,其中包含var變量語(yǔ)句。變量在執(zhí)行任何語(yǔ)句之前通過(guò)聲明階段,并立即通過(guò)作用域開(kāi)始處的初始化階段(步驟1)。函數(shù)作用域中var變量語(yǔ)句的位置不影響聲明和初始化階段。
在聲明和初始化之后,但在賦值階段之前,變量具有undefined 的值,并且已經(jīng)可以使用。
在賦值階段variable = "value" 時(shí),變量接收它的初值(步驟2)。
嚴(yán)格意義的提升是指在函數(shù)作用域的開(kāi)始處聲明并初始化一個(gè)變量。聲明階段和初始化階段之間沒(méi)有差別。
讓我們來(lái)研究一個(gè)例子。下面的代碼創(chuàng)建了一個(gè)包含var語(yǔ)句的函數(shù)作用域
function multiplyByTen(number) { console.log(ten); // => undefined var ten; ten = 10; console.log(ten); // => 10 return number * ten; } multiplyByTen(4); // => 40
開(kāi)始執(zhí)行multipleByTen(4)并進(jìn)入函數(shù)作用域時(shí),變量ten在第一個(gè)語(yǔ)句之前通過(guò)聲明和初始化步驟。因此,當(dāng)調(diào)用console.log(ten)時(shí),打印undefined。語(yǔ)句ten = 10指定一個(gè)初值。賦值之后,console.log(ten) 將正確地打印10。
4. 函數(shù)聲明生命周期在函數(shù)聲明語(yǔ)句function funName() {...}的情況下,它比變量聲明生命周期更簡(jiǎn)單。
聲明、初始化和賦值階段同時(shí)發(fā)生在封閉函數(shù)作用域的開(kāi)頭(只有一步)??梢栽谧饔糜虻娜魏挝恢谜{(diào)用funName(),而不依賴于聲明語(yǔ)句的位置(甚至可以在末尾調(diào)用)。
下面的代碼示例演示了函數(shù)提升:
function sumArray(array) { return array.reduce(sum); function sum(a, b) { return a + b; } } sumArray([5, 10, 8]); // => 23
當(dāng)執(zhí)行sumArray([5,10,8])時(shí),它進(jìn)入sumArray函數(shù)作用域。在這個(gè)作用域內(nèi),在任何語(yǔ)句執(zhí)行之前,sum都會(huì)通過(guò)所有三個(gè)階段:聲明、初始化和賦值。這樣,array.reduce(sum)甚至可以在它的聲明語(yǔ)句sum(a, b){…}之前使用sum。
5. let 變量的生命周期let 變量的處理方式與var不同,主要區(qū)別在于聲明和初始化階段是分開(kāi)的。
現(xiàn)在來(lái)看看一個(gè)場(chǎng)景,當(dāng)解釋器進(jìn)入一個(gè)包含let變量語(yǔ)句的塊作用域時(shí)。變量立即通過(guò)聲明階段,在作用域中注冊(cè)其名稱(步驟1)。
然后解釋器繼續(xù)逐行解析塊語(yǔ)句。
如果在此階段嘗試訪問(wèn)變量,JS 將拋出 ReferenceError: variable is not defined。這是因?yàn)樽兞繝顟B(tài)未初始化,變量位于暫時(shí)死區(qū) temporal dead zone。
當(dāng)解釋器執(zhí)行到語(yǔ)句let variable時(shí),傳遞初始化階段(步驟2)。變量退出暫時(shí)死區(qū)。
接著,當(dāng)賦值語(yǔ)句variable = "value"出現(xiàn)時(shí),將傳遞賦值階段(步驟3)。
如果JS 遇到let variable = "value",那么初始化和賦值將在一條語(yǔ)句中發(fā)生。
讓我們看一個(gè)例子,在塊作用域中用 let 聲明變量 number
let condition = true; if (condition) { // console.log(number); // => Throws ReferenceError let number; console.log(number); // => undefined number = 5; console.log(number); // => 5 }
當(dāng) JS 進(jìn)入if (condition) {...} 塊作用域,number立即通過(guò)聲明階段。
由于number已經(jīng)處于單一化狀態(tài),并且處于的暫時(shí)死區(qū),因此訪問(wèn)該變量將引發(fā)ReferenceError: number is not defined。接著,語(yǔ)句let number進(jìn)行初始化?,F(xiàn)在可以訪問(wèn)變量,但是它的值是undefined。
const和class 類型與let具有相同的生命周期,只是分配只能發(fā)生一次。
5.1 提升在let生命周期中無(wú)效的原因如上所述,提升是變量在作用域頂部的耦合聲明和初始化階段。然而,let生命周期分離聲明和初始化階段。解耦消除了let的提升期限。
這兩個(gè)階段之間的間隙產(chǎn)生了暫時(shí)死區(qū),在這里變量不能被訪問(wèn)。
總結(jié)使用var聲明變量很容易出錯(cuò)。在此基礎(chǔ)上,ES6 引入了let。它使用一種改進(jìn)的算法來(lái)聲明變量,并附加了塊作用域。
由于聲明和初始化階段是解耦的,提升對(duì)于let變量(包括const和class)無(wú)效。在初始化之前,變量處于暫時(shí)死區(qū),不能訪問(wèn)。
為了保持變量聲明的流暢性,建議使用以下技巧
聲明、初始化然后使用變量,這個(gè)流程是正確的,易于遵循。
盡量隱藏變量。公開(kāi)的變量越少,代碼就越模塊化。
番外 如何理解 let x = x 報(bào)錯(cuò)之后,再次 let x 依然會(huì)報(bào)錯(cuò)?這個(gè)問(wèn)題說(shuō)明:如果 let x 的初始化過(guò)程失敗了,那么
x 變量就將永遠(yuǎn)處于 created 狀態(tài)。
你無(wú)法再次對(duì) x 進(jìn)行初始化(初始化只有一次機(jī)會(huì),而那次機(jī)會(huì)你失敗了)。
由于 x 無(wú)法被初始化,所以 x 永遠(yuǎn)處在暫時(shí)死區(qū)
有人會(huì)覺(jué)得 JS 坑,怎么能出現(xiàn)這種情況;其實(shí)問(wèn)題不大,因?yàn)榇藭r(shí)代碼已經(jīng)報(bào)錯(cuò)了,后面的代碼想執(zhí)行也沒(méi)機(jī)會(huì)。
參考:
我用了兩個(gè)月的時(shí)間才理解 let
交流干貨系列文章匯總?cè)缦?,覺(jué)得不錯(cuò)點(diǎn)個(gè)Star,歡迎 加群 互相學(xué)習(xí)。
https://github.com/qq44924588...
我是小智,公眾號(hào)「大遷世界」作者,對(duì)前端技術(shù)保持學(xué)習(xí)愛(ài)好者。我會(huì)經(jīng)常分享自己所學(xué)所看的干貨,在進(jìn)階的路上,共勉!
關(guān)注公眾號(hào),后臺(tái)回復(fù)福利,即可看到福利,你懂的。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/105731.html
摘要:請(qǐng)注意,就變量生命周期而言,聲明階段與變量聲明是不同的概念。提升在生命周期中無(wú)效的原因如上所述,提升是變量在作用域頂部的耦合聲明和初始化階段。然而,生命周期分離聲明和初始化階段。解耦消除了的提升期限。 為了保證的可讀性,本文采用意譯而非直譯。 提升是將變量或函數(shù)定義移動(dòng)到作用域頭部的過(guò)程,通常是 var 聲明的變量和函數(shù)聲明function fun() {...}。 當(dāng) ES6 引入l...
摘要:不同的是函數(shù)體并不會(huì)再被提升至函數(shù)作用域頭部,而僅會(huì)被提升到塊級(jí)作用域頭部避免全局變量在計(jì)算機(jī)編程中,全局變量指的是在所有作用域中都能訪問(wèn)的變量。 ES6 變量作用域與提升:變量的生命周期詳解從屬于筆者的現(xiàn)代 JavaScript 開(kāi)發(fā):語(yǔ)法基礎(chǔ)與實(shí)踐技巧系列文章。本文詳細(xì)討論了 JavaScript 中作用域、執(zhí)行上下文、不同作用域下變量提升與函數(shù)提升的表現(xiàn)、頂層對(duì)象以及如何避免創(chuàng)建...
摘要:生命周期假設(shè)這樣一個(gè)場(chǎng)景當(dāng)解釋器剛進(jìn)入一個(gè)包含的作用域時(shí),則在任何語(yǔ)句執(zhí)行之前,變量就已完成了聲明階段和初始化階段,且值為。當(dāng)解釋器執(zhí)行完,變量就已完成了初始化階段,離開(kāi)了臨時(shí)死區(qū),并具有的值。 變量提升是一個(gè)將變量聲明或者函數(shù)聲明提升到作用域起始處的過(guò)程。在本篇博文中,我們一起深入了解這個(gè)過(guò)程的更多細(xì)節(jié)。 變量的生命周期 當(dāng)引擎使用變量時(shí),它們的生命周期包含以下階段: 聲明階段,...
摘要:最近在上看到一篇關(guān)于變量提升的文章,原文在此。對(duì)于剛?cè)腴T(mén)的開(kāi)發(fā)者時(shí)常難以理解變量方法提升的獨(dú)特行為。接下來(lái)我們要談?wù)?,,聲明,那么先了解變量提升就顯得更為重要了。在進(jìn)入作用域和不能訪問(wèn)的這段時(shí)間,我們稱為暫時(shí)性死區(qū)。 showImg(https://segmentfault.com/img/bV0Nsd?w=800&h=450); 最近在Medium上看到一篇關(guān)于變量提升的文章,原文在...
摘要:面試題來(lái)源于網(wǎng)絡(luò),看一下高級(jí)前端的面試題,可以知道自己和高級(jí)前端的差距。 面試題來(lái)源于網(wǎng)絡(luò),看一下高級(jí)前端的面試題,可以知道自己和高級(jí)前端的差距。有些面試題會(huì)重復(fù)。 使用過(guò)的koa2中間件 koa-body原理 介紹自己寫(xiě)過(guò)的中間件 有沒(méi)有涉及到Cluster 介紹pm2 master掛了的話pm2怎么處理 如何和MySQL進(jìn)行通信 React聲明周期及自己的理解 如何...
閱讀 2492·2021-11-24 10:26
閱讀 2662·2021-11-16 11:44
閱讀 1795·2021-09-22 15:26
閱讀 3655·2021-09-10 11:11
閱讀 3263·2021-09-07 10:25
閱讀 3710·2021-09-01 10:41
閱讀 1086·2021-08-27 13:11
閱讀 3561·2021-08-16 11:02