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