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