摘要:想閱讀更多優(yōu)質(zhì)文章請(qǐng)猛戳博客一年百來篇優(yōu)質(zhì)文章等著你引用規(guī)范作者一條最近的推特變量提升是一個(gè)陳舊且令人困惑的術(shù)語。變量提升部分提前激活是在和之前聲明變量的一種較老的方法。
為了保證可讀性,本文采用意譯而非直譯。
想閱讀更多優(yōu)質(zhì)文章請(qǐng)猛戳GitHub博客,一年百來篇優(yōu)質(zhì)文章等著你!
引用 ES6 規(guī)范作者 Allen Wirfs-Brock一條最近的推特:
變量提升是一個(gè)陳舊且令人困惑的術(shù)語。甚至在 ES6
之前:變量提升的意思究竟是“提升至當(dāng)前作用域頂部”還是“從嵌套的代碼塊中提升到最近的函數(shù)或腳本作用域中”?還是兩者都有?
受 Allen 啟發(fā),本文提出了一種不同的方法來描述變量聲明。
1. 聲明:作用域與激活可以將聲明分為兩個(gè)方面:
作用域:在哪里可以看到聲明的變量? 這是一個(gè)靜態(tài)特征。
激活:我什么時(shí)候可以訪問變量? 這是一個(gè)動(dòng)態(tài)特征:有些變量只要我們進(jìn)入其作用域,就可以訪問。 有的,我們必須等到執(zhí)行到它們的聲明。
下表總結(jié)了不同聲明的方式如何處理上述兩個(gè)方面。
“Duplicates”描述是否可以在同一作用域內(nèi)聲明兩次。
“Global prop.”表示一個(gè)在 script 中的聲明,當(dāng)全局作用域中被執(zhí)行時(shí),是否會(huì)向全局對(duì)象添加屬性。
TDZ 表示暫時(shí)性死區(qū)(稍后解釋)。 函數(shù)聲明在嚴(yán)格模式下是塊作用域的(例如在模塊內(nèi)部),但在非嚴(yán)格模式下是函數(shù)作用域。
2. const 和 let :暫時(shí)性死區(qū)對(duì)于JavaScript,TC39 需要決定如果在聲明之前訪問其直接作用域中的常量會(huì)發(fā)生什么:
{ console.log(x); // 這里會(huì)發(fā)生什么? const x; }
主要有兩種種情況:
打印 undefined
報(bào)錯(cuò)
第一種不會(huì)出現(xiàn),因?yàn)?x 是一個(gè)常量,如果打印 undefined,在聲明前和聲明后它將擁有不同的值,x 就不是常量了。
let 和 const 都會(huì)出現(xiàn)第二種情況,就是會(huì)報(bào)錯(cuò)。進(jìn)入變量作用域與執(zhí)行聲明之間的這段時(shí)間被稱為該變量的 臨時(shí)死區(qū)(TDZ):
在臨時(shí)死區(qū)中,變量被認(rèn)為是未初始化的(就像它有一個(gè)特殊的值一樣)。
如果訪問未初始化的變量,將得到ReferenceError 錯(cuò)誤。
一旦執(zhí)行到變量聲明,該變量將被設(shè)置為初始化器的值(通過賦值符號(hào)指定),如果沒有初始化,則為undefined。
以下代碼說明了臨時(shí)死區(qū):
if (true) { // 進(jìn)入 `tmp` 的作用域,TDZ 開始 // `tmp` 未被初始化: assert.throws(() => (tmp = "abc"), ReferenceError); assert.throws(() => console.log(tmp), ReferenceError); let tmp; // TDZ 結(jié)束 assert.equal(tmp, undefined); }
下一個(gè)例子表明臨時(shí)死區(qū)只是 暫時(shí)的 (與時(shí)間有關(guān)):
if (true) { // 進(jìn)入 `myVar` 作用域,TDZ 開始 const func = () => { console.log(myVar); // 稍后執(zhí)行 }; // 我們?cè)?TDZ 中: // 訪問 `myVar` 造成 `ReferenceError` let myVar = 3; // TDZ 結(jié)束 func(); // OK,在 TDZ 外調(diào)用 }
即使 func() 位于myVar聲明之前使用 myVar 變量,但我們也可以調(diào)用func(),前提是必須等到myVar的臨時(shí)死區(qū)結(jié)束。
函數(shù)聲明與提前激活函數(shù)聲明總是在進(jìn)入它的作用域時(shí)執(zhí)行,不管它位于作用域的什么位置。這使得能夠在函數(shù)foo()聲明之前調(diào)用它:
assert.equal(foo(), 123); // ok,相等 function foo() { return 123; }
提前激活 foo()意味著樓上的代碼等價(jià)于
function foo() { return 123; } assert.equal(foo(), 123);
如果用 const 或 let 聲明一個(gè)函數(shù),它就不會(huì)被提前激活:在下面的例子中,你只能在 bar() 聲明后調(diào)用它。
assert.throws( () => bar(), // 聲明前 ReferenceError); const bar = () => { return 123; }; assert.equal(bar(), 123); // 聲明后在沒有提前激活的情況下提前調(diào)用
即使函數(shù)g()沒有提前激活,也可以被前面的函數(shù) f()(在同一作用域內(nèi))調(diào)用 - 只要遵守以下規(guī)則:f() 必須在聲明 g() 之后調(diào)用
const f = () => g(); const g = () => 123; // g() 聲明后調(diào)用 f(): assert.equal(f(), 123);
模塊中的函數(shù)通常在模塊執(zhí)行完后調(diào)用。 因此,在模塊中,很少需要擔(dān)心函數(shù)的順序。
最后,注意提前激活是怎樣自動(dòng)執(zhí)行以維持上述規(guī)則的:當(dāng)進(jìn)入一個(gè)作用域時(shí),在任何函數(shù)被調(diào)用前,所有的函數(shù)聲明都會(huì)被先執(zhí)行。
提前激活的一個(gè)陷阱如果依賴于提前激活機(jī)制,在函數(shù)聲明之前調(diào)用函數(shù),那么需要注意的是它不會(huì)訪問未提前激活的變量。如下:
funcDecl(); const MY_STR = "abc"; function funcDecl() { console.log(MY_STR) }
上述會(huì)報(bào)錯(cuò):
如果你在 MY_STR 聲明之后調(diào)用 funcDecl() 就不會(huì)有問題。
提前激活的利弊我們已經(jīng)看到提前激活有一個(gè)陷阱,你可以在不使用它的情況下獲得大部分好處。因此,最好避免提前激活。但我對(duì)此說法并非十分認(rèn)同,如前所述,我經(jīng)常使用函數(shù)聲明,因?yàn)槲蚁矚g它們的語法。
類聲明不會(huì)提前激活類聲明不會(huì)提前激活:
assert.throws( () => new MyClass(), ReferenceError); class MyClass {} assert.equal(new MyClass() instanceof MyClass, true);
這是為什么? 考慮以下類聲明:
class MyClass extends Object {}
extends是可選的,它的操作數(shù)是一個(gè)表達(dá)式。 因此,您可以這樣做:
const identity = x => x; class MyClass extends identity(Object) {}
計(jì)算這樣的表達(dá)式必須在它被引用的地方完成,其它行為都會(huì)使人困惑。這解釋了為什么類聲明不提前激活。
var :變量提升(部分提前激活)var是在const和let之前聲明變量的一種較老的方法??紤]下面的var聲明。
var x = 123;
這個(gè)聲明包含兩個(gè)部分:
聲明var x:與大多數(shù)其他聲明一樣,var聲明變量的作用域是最內(nèi)層的包圍函數(shù),而不是最內(nèi)層的包圍塊。這樣的變量在其作用域的開始時(shí)就已處于活動(dòng)狀態(tài),并使用undefined初始化。
賦值 x = 123 :賦值總是在適當(dāng)位置執(zhí)行。
以下代碼演示了 var :
function f() { // 部分提前激活: assert.equal(x, undefined); if (true) { var x = 123; // 賦值已經(jīng)執(zhí)行 assert.equal(x, 123); } // 作用域?yàn)楹瘮?shù)作用域,非塊級(jí)作用域。 assert.equal(x, 123); }交流
干貨系列文章匯總?cè)缦?,覺得不錯(cuò)點(diǎn)個(gè)Star,歡迎 加群 互相學(xué)習(xí)。
https://github.com/qq44924588...
我是小智,公眾號(hào)「大遷世界」作者,對(duì)前端技術(shù)保持學(xué)習(xí)愛好者。我會(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://systransis.cn/yun/75150.html
摘要:想閱讀更多優(yōu)質(zhì)文章請(qǐng)猛戳博客一年百來篇優(yōu)質(zhì)文章等著你引用規(guī)范作者一條最近的推特變量提升是一個(gè)陳舊且令人困惑的術(shù)語。變量提升部分提前激活是在和之前聲明變量的一種較老的方法。 為了保證可讀性,本文采用意譯而非直譯。 想閱讀更多優(yōu)質(zhì)文章請(qǐng)猛戳GitHub博客,一年百來篇優(yōu)質(zhì)文章等著你! 引用 ES6 規(guī)范作者 Allen Wirfs-Brock一條最近的推特: 變量提升是一個(gè)陳舊且令人困惑的...
摘要:云計(jì)算有哪些技術(shù)特點(diǎn)很多技術(shù)宅的專家們?cè)?jīng)總結(jié)過云計(jì)算技術(shù)的一些特點(diǎn),下面我們就來概述一下。目前,云計(jì)算技術(shù)在安全問題上已經(jīng)有了長足的進(jìn)步,通過對(duì)數(shù)據(jù)傳輸數(shù)據(jù)存儲(chǔ)數(shù)據(jù)審計(jì)三個(gè)環(huán)節(jié)采取相應(yīng)的安全措施,從而保障整個(gè)云平臺(tái)的安全?! ≡朴?jì)算當(dāng)前處于何種現(xiàn)狀? 我們?cè)谡勅魏我环N新技術(shù)的時(shí)候,用戶往往更關(guān)注技術(shù)所在領(lǐng)域的應(yīng)用程度等問題,對(duì)于云計(jì)算來說,近些年在科研領(lǐng)域的應(yīng)用已經(jīng)取得了突飛猛進(jìn)的進(jìn)展,...
摘要:區(qū)塊鏈水平擴(kuò)容的基本思想是將單根區(qū)塊鏈的狀態(tài)劃分為多條區(qū)塊鏈狀態(tài)。通過增加網(wǎng)絡(luò)中片的數(shù)量,整個(gè)區(qū)塊鏈網(wǎng)絡(luò)的吞吐量將會(huì)線性增加。的宗旨是通過以分片為代表的水平擴(kuò)容技術(shù),建立一個(gè)人人可用的區(qū)塊鏈底層公鏈。 目前,公鏈極低的交易處理能力(TPS)為人們便捷的使用區(qū)塊鏈帶來很大的麻煩。例如:比特幣網(wǎng)絡(luò)只支持6到7個(gè)TPS,而以太坊目前只能處理大約15 TPS,而中心化支付系統(tǒng)的代表:支付寶,在...
摘要:數(shù)組原理遍歷原理揭秘?cái)?shù)組原理遍歷原理揭秘可見,數(shù)組其實(shí)已經(jīng)改變了,但是遍歷出來的并沒有增加的哪一項(xiàng)。此時(shí),我們也可以輸出一下當(dāng)前指針位置數(shù)組原理遍歷原理揭秘?cái)?shù)組原理遍歷原理揭秘?cái)?shù)組指針停留在了位置上。 php中的中的數(shù)組跟js里面數(shù)組是不大一樣的。php中數(shù)組的下標(biāo)可以整數(shù)也可以是字符串,而且數(shù)組中元素的順序不是由下標(biāo)決定的,而是由添加元素的順序。數(shù)組基礎(chǔ) $arr1 = array(...
閱讀 3025·2021-11-22 12:06
閱讀 605·2021-09-03 10:29
閱讀 6559·2021-09-02 09:52
閱讀 2024·2019-08-30 15:52
閱讀 3420·2019-08-29 16:39
閱讀 1198·2019-08-29 15:35
閱讀 2071·2019-08-29 15:17
閱讀 1427·2019-08-29 11:17