你想在在變量聲明以前就使用變量?之後再也別這樣作了。javascript
新的聲明方式(let,const)較之以前的聲明方式(var),還有一個區別,就是新的方式不容許在變量聲明以前就使用該變量,可是var是能夠得。請看下面的代碼,下面這個代碼是能夠正常運行的:java
function func() { console.log(localVariable); // undefined var localVariable = 5; console.log(localVariable); // 5 } func();
可是這種卻不能夠函數
function func() { console.log(localVariable); // ReferenceError: localVariable is not defined let localVariable = 10; console.log(localVariable); // 10 } func();
等下,咱們上一章曾經介紹了一個叫「提高」的概念,它會吧全部的變量定義在做用域的最前面。這是否意味着若是我不在實際的定義以前使用變量,而後就不會有提高了呢?答案是否認的。提高依然會有,而且適用於全部類型的變量類型。可是const和let卻不是這樣的。翻譯
首先,咱們看一下var關鍵字是怎麼工做的。規範對其是這樣進行的描述的。調試
var聲明定義了在正在運行的執行上下文(running execution
context)做用域內的變量環境(VariableEnvironment中)的變量。var變量在當包含的詞法環境(Lexical
Environment)初始化時被建立,在建立的時候被賦值爲undefined。[...]
在執行VariableDeclaration時,由帶有Initializer的VariableDeclaration定義的變量被賦其設定項的Initializer's
AssignmentExpression的值。
規範中有許多的細節,讓咱們簡單的來看一下:code
當你進入到一個做用域中,在內部被定義的全部的變量都會被建立。blog
全部存在的變量,均可以被訪問,而且會把undefined賦值給該變量。ip
當代碼(執行時)到達初始化時,會被分配給一個實際的值。作用域
咱們來看一下規範中對let和const的表述:get
let和const聲明是定義在當前執行上下文做用域中的詞法環境中的變量。當包含的詞法環境被初始化的時候,變量被建立。可是在變量的詞法綁定時被計算以前是不容許經過任何方式來訪問的。當詞法綁定計算時而不是在變量被建立的時候,由詞法綁定定義的變量的初始值被被賦予賦值表達式的值(也就是「=」右邊的表達式)。當詞法綁定被計算的時候,若是let聲明中沒有初始化的值的時候(也就是「let
a;」這樣的形式),會被賦值爲undefined。
簡單來講:
若是你進入到了指定的做用域中,它裏面定義的全部的變量都會被初始化,這一點和var很像。
這裏有一個不一樣點:像var同樣,全部的變量都會存在,可是他們目前還不能被訪問(裏面沒有值,甚至是undefined)。
若是let變量在相同的地方被定義和初始化,他們會被賦予合適的值,反之,變量就是undefined。const變量必須在定義的時候初始化。
咱們來看一些相關的例子。
實際上,這種描述引出了咱們的另外一個定義。他很讓人可怕,由於他叫:臨時死區(TDZ)。這個屬於明確了一個咱們沒法訪問咱們的變量的代碼的區域。咱們來看一下下面的代碼和相關聯的註釋,來簡單的解釋一下TDZ是什麼。
function func() { // Start of TDZ for deadVariable // we can still do something here, just our deadVariable is not available yet const exampleVariable = 5; console.log(exampleVariable); // 5 // End of TDZ for deadVariable let deadVariable = 10; console.log(deadVariable); // 10 } func();
有一件事情值得去提醒。就是對於名字的建議,這是一個臨時死區,意思這個區域是由時間定義的,而不是位置。所以當運行代碼的時候,你的聲明在被JS解析器解析以前是不能被訪問的。所以你把使用的變量的位置放在哪裏並不重要,只要是在聲明執行後訪問該變量就能夠。因此看下面的代碼:
function func() { return deadOrAlive; } let deadOrAlive = 'alive!' console.log(func()); // alive!
這是運行代碼的步驟:
函數被聲明
變量deadOrAlive被聲明,而且初始化了一個值「alive」
如今咱們調用咱們的函數。
因爲變量deadOrAlive已經被聲明,是可訪問的,所以會打印出正確的結果 「alive」。
可是下面的例子卻會報錯,思考一下緣由。
function func() { return deadOrAlive; } console.log(func()); // ReferenceError: deadOrAlive is not defined let deadOrAlive = 'dead!'
因此TDZ是一個避免因先使用後聲明而致使的一些詭異的bug而出現的一個很好機制(具體看「提高」相關內容)。咱們不須要去額外作什麼事情,就是記住永遠不要在變量聲明以前使用這個變量。即便咱們這樣作了,咱們也會獲得一個很好的報錯信息。只有一個條件-你必須使用let或者是const來替換掉var。
var和let,const的另外一個區別是 - 後者僅僅能夠被定義一次。而對於var的話,若是被同時定義屢次,程序也依然會很好的運行。
var doubledVariable = 5; var doubledVariable = 6; console.log(doubledVariable); // 6
可是如今,當你用let和const來作一樣的事情,就會獲得一個語法錯誤:
let doubledVariable = 5; let doubledVariable = 6; // SyntaxError: Identifier 'doubledVariable' has already been declared
可是,在嵌套的塊級做用域中,使用相同名字的變量依然會很好的工做的,這個我想你們已經清楚了,就不用過多解釋了吧。
let doubledVariable = 5; if (true) { let doubledVariable = 6; console.log(doubledVariable); // 6 } console.log(doubledVariable); // 5
不能重複定義這個功能其實是頗有用的,能夠組織不少bug的發生。好比說你曾經在一個函數內,在不一樣地方用var定義了多個相同名稱的變量,此時以前定義的變量可能會被覆蓋,這樣對於代碼來講無疑是一個隱患,也就是由於這樣,這個特性其實是一個簡單的,開箱即用的解決方案。
總結一下,在ES6中有兩種新方法來聲明變量:經過let和const關鍵字,除此以外,二者都是塊級做用域,而且在聲明以前不能訪問該變量。與以前的var相比是一個主要的升級。而且會消除你不少的困擾。我提出了幾個例子,可能會幫助你節省了很多調試的時間,可是還有更多。若是你感興趣的話,能夠在網上簡單的搜索一下。好久以前,我我的曾建議中止使用var關鍵字,因此如今個人代碼裏充滿了let和const。我建議你也是這樣,在之後當你想改變變量的值,就使用let和const。不要再使用var了。
本文翻譯自: