摘要:是一系列關(guān)于在標(biāo)準(zhǔn)的第六版中加入編程語言的新功能,簡稱。因此,雖然警報(bào)可見,但輸入事件不會傳遞。即使標(biāo)準(zhǔn)委員會也沒有權(quán)力,比如用的自動分號插入修正奇怪的怪癖。這個變量在范圍內(nèi)但未初始化的時(shí)期稱為時(shí)間盲區(qū)。標(biāo)準(zhǔn)委員會考慮使用這種范圍規(guī)則來讓。
ES6 In Depth是一系列關(guān)于在ECMAScript標(biāo)準(zhǔn)的第六版中加入JavaScript編程語言的新功能,簡稱ES6。
我今天想談的這個特點(diǎn)既簡單又令人感到驚喜。
當(dāng)Brendan Eich在1995年設(shè)計(jì)了JavaScript的第一個版本時(shí),其中有很多問題,包括自此以后一直是該語言的一部分的東西,比如Date對象和對象在意外地相乘時(shí)會自動轉(zhuǎn)換為NaN。然而,事后看來,他所做的事情是非常重要的事情:_objects_; _prototypes_;_function_和_作用域_;可變數(shù)據(jù)類型。這門語言有很好的結(jié)構(gòu)。比任何人一開始意識到的要好。
盡管如此,Brendan 還是做了一個特別的設(shè)計(jì)決定 - 這個決定我認(rèn)為是一個錯誤。這是一件小事。一個微妙的東西。您可能會使用該語言多年,甚至沒有注意到它。但它很重要,因?yàn)檫@個錯誤在我們現(xiàn)在認(rèn)為是“好的部分”的語言中。
它與變量有關(guān)
問題#1:塊不是范圍規(guī)則聽起來很合理: JS函數(shù)中聲明的var的作用域是該函數(shù)的整個主體。 但是有兩種方式會導(dǎo)致不同的后果。
一個是塊中聲明的變量范圍不僅僅是塊。是整個函數(shù)。
你以前可能從未注意到這一點(diǎn)??峙逻@是你無法看不到的東西之一。讓我們通過一個會導(dǎo)致棘手問題的場景來說明這個bug。
假設(shè)您有一段代碼是使用名為t的變量:
function runTowerExperiment(tower, startTime) { var t = startTime; tower.on("tick", function () { ... code that uses t ... }); ... more code ... }
到目前為止,一切都很好?,F(xiàn)在你想添加保齡球速度測量,所以你添加一個if語句到內(nèi)部回調(diào)函數(shù)。
function runTowerExperiment(tower, startTime) { var t = startTime; tower.on("tick", function () { ... code that uses t ... if (bowlingBall.altitude() <= 0) { var t = readTachymeter(); ... } }); ... more code ... }
你無意中添加了第二個名為t的變量?,F(xiàn)在,在“使用t的代碼”之前工作得很好,t指的是新的內(nèi)部變量t,而不是現(xiàn)有的外部變量。
JavaScript中var的范圍就像Photoshop中的繪畫工具。它從聲明的兩個方向延伸,向前和向后,并且它繼續(xù)前進(jìn),直到它到達(dá)函數(shù)邊界。由于這個變量的范圍向后延伸,所以只要我們進(jìn)入函數(shù)就必須創(chuàng)建它。這被稱為提升。我喜歡想象JS引擎將每個變量和函數(shù)用一個小代碼起重機(jī)提升到封閉函數(shù)的頂部。
現(xiàn)在,變量提升有其好處。沒有它,很多在全局范圍內(nèi)工作得很好的完美適用的技術(shù)在 IIFE內(nèi)部不起作用。但是在這種情況下,提升造成了一個令人討厭的bug:所有使用t的計(jì)算都將開始生成NaN。這也很難追查,特別是如果你的代碼比這個例子更大的時(shí)候。
添加一個新的代碼塊導(dǎo)致該塊之前的代碼中出現(xiàn)神秘錯誤。我們不希望效果先于原因。
但與第二個var問題相比,這還只是一個小問題。
問題#2:循環(huán)中的變量泛濫你可以猜測當(dāng)你運(yùn)行這段代碼時(shí)會發(fā)生什么。這非常簡單:
var messages = ["Hi!", "I"m a web page!", "alert() is fun!"]; for (var i = 0;i < messages.length;i++) { alert(messages[i]); }
如果你一直在關(guān)注這個系列,你就知道我喜歡用alert()作為代碼。也許你也知道alert()是一個糟糕的API。它是同步的。因此,雖然警報(bào)可見,但輸入事件不會傳遞。您的JS代碼 - 實(shí)際上是您的整個UI - 基本上都會暫停,直到用戶單擊確定。(譯者: alert具有阻塞性)
所有這些都讓alert()幾乎成為了你想要在網(wǎng)頁中做的任何事情的錯誤選擇。我使用它是因?yàn)槲艺J(rèn)為所有這些相同的東西都使alert()成為一個很好的教學(xué)工具。
盡管如此,我還是可以說服自己放棄所有這些笨拙和不良行為......如果這意味著我可以做一個說話的貓。
var messages = ["Meow!", "I"m a talking cat!", "Callbacks are fun!"]; for (var i = 0; i < messages.length; i++) { setTimeout(function () { cat.say(messages[i]); }, i * 1500); }
看到這個代碼工作不正確!
B但有些事情是錯的。貓沒有按順序說出所有三條消息,而是三次說“undefined”。
你能發(fā)現(xiàn)錯誤嗎?
這里的問題是,我只有一個變量。它由循環(huán)本身和所有三個超時(shí)回調(diào)共享。當(dāng)循環(huán)結(jié)束運(yùn)行時(shí),i的值為3(因?yàn)閙essages.length為3),并且還沒有調(diào)用任何回調(diào)。(譯者:可以去了解一下Event loop)
所以當(dāng)?shù)谝淮纬瑫r(shí)觸發(fā)并調(diào)用cat.say(messages [i])時(shí),它使用消息[3]。那個當(dāng)然是undefined的。
有很多方法可以解決這個問題(這里是一個),這是由var范圍規(guī)則引起的第二個問題。
let 是新的 var大多數(shù)情況下,JavaScript中的設(shè)計(jì)錯誤(其他編程語言,特別是JavaScript)也無法修復(fù)。向后兼容意味著永遠(yuǎn)不要改變Web上現(xiàn)有JS代碼的行為。即使標(biāo)準(zhǔn)委員會也沒有權(quán)力,比如用JavaScript的自動分號插入修正奇怪的怪癖。瀏覽器制造商根本不會實(shí)施突破性的改變,因?yàn)檫@種改變懲罰了用戶。
大約十年前,當(dāng)Brendan Eich決定解決這個問題時(shí),實(shí)際上只有一種方法可以解決這個問題。
他添加了一個新的關(guān)鍵字let,它可以用來聲明變量,就像var一樣,但是具有更好的范圍規(guī)則。
它看起來像這樣:
let t = readTachymeter();
或則這樣:
for (let i = 0;i < messages.length;i++) { ... }
let和var是不同的,所以如果你只是在整個代碼中進(jìn)行全局搜索和替換,那可能會破壞你的代碼的一部分(可能無意)依賴于var的怪癖。但絕大多數(shù)情況下,在新的ES6代碼中,您應(yīng)該停止使用var并使用let來代替。因此口號是:“l(fā)et is the new var”。
let和var之間究竟有什么區(qū)別?很高興你有這樣的疑問!
let 變量是 block-scope. 用let聲明的變量的作用域就是封閉塊,而不是整個封閉函數(shù)。
runTowerExperiment示例可以通過將var更改為let來修復(fù)。如果你在任何地方使用let,你將永遠(yuǎn)不會有這樣的錯誤。
全局let變量不是全局對象的屬性。也就是說,你不會通過編寫window.variableName來訪問它們。相反,他們生活在一個隱形塊的范圍內(nèi),這個塊在概念上包含了所有在網(wǎng)頁中運(yùn)行的JS代碼。
(let x ...)形式的循環(huán)在每次迭代中為x創(chuàng)建一個新的綁定。.
這是一個非常微妙的差異。這意味著如果一個for(let ...)循環(huán)執(zhí)行多次,并且該循環(huán)包含一個閉包,就像我們在談?wù)撠埖睦又幸粯?,每個閉包將捕獲循環(huán)變量的不同副本,而不是捕獲相同的閉包循環(huán)變量。
所以說話的貓例子也可以通過改變var來解決。 這適用于所有三種for循環(huán):for-of, for-in,以及帶分號的舊式C類。
在聲明之前嘗試使用let變量是錯誤的。 在控制流到達(dá)聲明的代碼行之前,該變量是未初始化的。例如:
function update() { console.log("current time:", t); // ReferenceError ... let t = readTachymeter(); }
這條規(guī)則可以幫助你發(fā)現(xiàn)錯誤。代替NaN結(jié)果,您會在問題所在的代碼行中發(fā)現(xiàn)異常。
這個變量在范圍內(nèi)但未初始化的時(shí)期稱為時(shí)間盲區(qū)。
(脆弱的性能細(xì)節(jié):在大多數(shù)情況下,通過查看代碼可以判斷聲明是否運(yùn)行,所以JavaScript引擎在每次訪問變量時(shí)都不需要執(zhí)行額外的檢查,以確保它已經(jīng)但是,在一個閉包中,有時(shí)候不清楚,在這種情況下,JavaScript引擎會執(zhí)行一次運(yùn)行時(shí)檢查,這意味著可以比var慢一點(diǎn)。)
(脆弱的作用域范圍詳細(xì)說明:在一些編程語言中,變量的范圍從聲明開始,而不是向后覆蓋整個封閉塊。標(biāo)準(zhǔn)委員會考慮使用這種范圍規(guī)則來讓。這樣,引用ReferenceError的t的使用根本就不在后面的let t的范圍內(nèi),所以它根本不會引用該變量,它可以引用在封閉范圍內(nèi)的at。方法在封閉或功能提升方面效果不佳,因此最終放棄了)。
用let重復(fù)聲明一個變量是一個SyntaxError(譯者:語法錯誤)。
這條規(guī)則也可以幫助你發(fā)現(xiàn)微不足道的錯誤。盡管如此,如果嘗試進(jìn)行全局let-to-var轉(zhuǎn)換,最有可能導(dǎo)致您遇到一些問題的區(qū)別,因?yàn)樗踔吝m用于全局let變量。
如果你有幾個腳本都聲明了相同的全局變量,那么最好繼續(xù)使用var。如果您切換到let,無論哪個腳本加載第二個將失敗并出現(xiàn)錯誤。
或者使用ES6模塊。但那又是別的故事。
(Crunchy語法細(xì)節(jié):let是嚴(yán)格模式代碼中的保留字,在非嚴(yán)格模式代碼中,為了向后兼容,您仍然可以聲明名為let的變量,函數(shù)和參數(shù),您可以編寫var let =" q")
除了這些差異之外,let和var幾乎是一樣的。
例如,聲明多個由逗號分隔的變量,并且它們都支持解構(gòu)
請注意,class聲明的行為如let,而不是var。如果多次加載包含類的腳本,則第二次重新聲明該類時(shí)會出現(xiàn)錯誤。
const(常量)對,還有一件事!
ES6還引入了第三個關(guān)鍵字,您可以使用let:const。
用const聲明的變量就像let,除了你不能指定給它們,除了它們被聲明的地方。這是一個SyntaxError。
const MAX_CAT_SIZE_KG = 3000; //
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/94907.html
摘要:和都能夠聲明塊級作用域,用法和是類似的,的特點(diǎn)是不會變量提升,而是被鎖在當(dāng)前塊中。聲明常量,一旦聲明,不可更改,而且常量必須初始化賦值。臨時(shí)死區(qū)臨時(shí)死區(qū)的意思是在當(dāng)前作用域的塊內(nèi),在聲明變量前的區(qū)域叫做臨時(shí)死區(qū)。 主要知識點(diǎn)有:var變量提升、let聲明、const聲明、let和const的比較、塊級綁定的應(yīng)用場景showImg(https://segmentfault.com/img...
摘要:學(xué)習(xí)筆記工作中常用到的語法只是簡單提及和,今天有空于是寫了這篇文章深入理解中的和數(shù)據(jù)結(jié)構(gòu),與其它數(shù)據(jù)結(jié)構(gòu)的互相轉(zhuǎn)換。的提供了新的數(shù)據(jù)結(jié)構(gòu)。本身是一個構(gòu)造函數(shù),用來生成數(shù)據(jù)結(jié)構(gòu)。 文中的內(nèi)容主要是來自于阮一峰的《ES6標(biāo)準(zhǔn)入門》(第三版)?!秾W(xué)習(xí)ES6筆記──工作中常用到的ES6語法》只是簡單提及Set和Map,今天有空于是寫了這篇文章──《深入理解:ES6中的Set和Map數(shù)據(jù)結(jié)構(gòu),M...
摘要:聲明聲明的語法與的語法一致??偨Y(jié)文章都是以深入理解讀書筆記形式,大部分引用書中的定義,加上作者的理解,樣例也做了調(diào)整,所有樣例都可以放到里運(yùn)行親自嘗試。 1.變量提升 使用 var 關(guān)鍵字聲明的變量,無論其實(shí)際聲明位置在何處,都會被視為聲明于所在函數(shù)的 頂部(如果聲明不在任意函數(shù)內(nèi),則視為在全局作用域的頂部)。這句話從字面上不難理解。 但是他是怎樣一個過程,為什么會這樣。當(dāng)你代...
摘要:沒有聲明的情況和都能夠聲明塊級作用域,用法和是類似的,的特點(diǎn)是不會變量提升,而是被鎖在當(dāng)前塊中。聲明常量,一旦聲明,不可更改,而且常量必須初始化賦值。臨時(shí)死區(qū)的意思是在當(dāng)前作用域的塊內(nèi),在聲明變量前的區(qū)域叫做臨時(shí)死區(qū)。 本章涉及3個知識點(diǎn),var、let、const,現(xiàn)在讓我們了解3個關(guān)鍵字的特性和使用方法。 var JavaScript中,我們通常說的作用域是函數(shù)作用域,使用var聲...
摘要:設(shè)置對象屬性只讀。提供了一個注冊機(jī)制,當(dāng)你注冊之后,就能在全局共享注冊表里面的。的注冊表和對象表很像,都是結(jié)構(gòu),只不過這個是值。語法只有一個參數(shù),返回的是從注冊表獲取全局共享的注意如果要防止命名重復(fù)問題,可以加上前綴。 還記得對象Object嗎? let obj = { a: 1 } 對象的格式: Object { key: value } 在ES5的時(shí)代,對象的key只能...
閱讀 3470·2019-08-30 13:15
閱讀 1407·2019-08-29 18:34
閱讀 834·2019-08-29 15:18
閱讀 3494·2019-08-29 11:21
閱讀 3253·2019-08-29 10:55
閱讀 3711·2019-08-26 10:36
閱讀 1876·2019-08-23 18:37
閱讀 1832·2019-08-23 16:57