❝做者:米卡面試
❞
let 和 const 是 ECMAScript6 新推出的特性,其中 let 是可以替代 var 的「標準」,因此咱們探討 var、let 和 const 的區別,首先應該知道 var 到底有什麼不規範的地方,或者是說有什麼弊端。數組
var 是 Javascript 用來定義變量的一個關鍵字,這是一個簡單的變量定義方式瀏覽器
var a = 0; 複製代碼
可是若是咱們在定義這個變量以前,查詢這個 a 的話,實際上是不會報錯誤的markdown
console.log(a); // undefined var a = 0; console.log(a); // 0 複製代碼
雖然它輸出了 undefined ,但這並非咱們想要的,咱們但願在變量初始化前,是沒法訪問這個變量的,這雖然是一個約束,可是能讓你的程序變得更可捉摸與維護。而 var 之因此能在var a = 0;
前被訪問,是由於這一句話在編譯的時候實際上是按如下的順序進行的:閉包
var a; console.log(a); // undefined a = 0; console.log(a); // 0 複製代碼
這就是爲何第二行 a 會打印出 undefined 的緣由了,這個叫作變量提高,使用 var 初始化變量的時候,在該做用域的開始,會先定義這個變量,再在後面進行賦值(初始化)。因此甚至你能夠這麼玩兒:編輯器
console.log(a); // undefined a = 0; console.log(a); // 0 var a; 複製代碼
這樣也是能夠成功打印的,因此你們看到這應該明白了 var 會對程序眼維護形成的困擾吧? a 明明一路看下來沒有看到的,竟然依舊正常使用?!!這個時候還須要從該做用域查看到底有沒有隱藏的var a;
若是沒有的話那還須要去做用域外邊尋找。函數
console.log(a); // undefined let a = 0; console.log(a); // 0 複製代碼
咱們再來看一下如下代碼oop
for(var i = 0; i < 5; i++){ var a = 0; } console.log(i); // 5 console.log(a); // 0 複製代碼
咱們定義在 for 循環塊中的變量 「i、a」 竟然在循環外都能被取到,這顯然並不規範。spa
那麼這裏能夠再給你們提一個面試常常也會提到的題目code
for (var i = 0; i < 5; i++) { setTimeout(function () { console.log(i); // 5 5 5 5 5 }, 300); } for (let j = 0; j < 5; j++) { setTimeout(function () { console.log(j); // 0 1 2 3 4 }, 300); } 複製代碼
幾乎如出一轍的代碼,就由於var、let的做用域區別,獲得的結果大不同。對於第一個for循環來講,i在全局範圍內都有效,在setTimeout函數中沒有找到i的定義,因此在全局裏才能找到變量i,在for循環執行完以後,event loop中的,setTimeout中的匿名函數開始執行,這個時候的全局i其實已是5了,因此所有打印出了5。 而對於第二個for循環來講,let有塊級做用域的概念,且值得注意的是for循環頭部的let聲明有一個特殊的行爲,這個行爲指出變量在循環過程當中不止被聲明一次,每次迭代都會被聲明。隨後的每一個迭代都會使用上一個迭代結束時的值來初始化這個變量。這使得咱們的j,每次執行其實都是塊級做用域中的不一樣的一個變量,天然就不是最終的5了。 因此若是在不能使用E6的時候,可使用閉包來建立一個新的做用域。這個閉包能保留對它被聲明的位置所處的做用域的引用,將i強行容留在自執行函數的做用域中使其不被回收。這樣就能獲取到每次的i值。
for (var i = 0; i < 5; i++) { (function (i) { setTimeout(function () { console.log(i) // 0 1 2 3 4 }, 300) })(i) } 複製代碼
這個很好理解,var是容許定義兩次的,不報錯誤。
var a = 0; console.log(a); // 0 var a = 1; console.log(a); // 1 複製代碼
編輯器會在判斷有已經聲明的同名變量時,會忽略 var 關鍵字,而後直接賦值,因此若是重複使用的一個聲明有一個初始值,那麼它是一個賦值語句。若是重複使用的一個聲明沒有一個初始值,那麼它不會對原來存在的變量有任何的影響。這對於 ES5 以前的 JS 是合法的,可是 ES6 後,認爲這種重複定義的作法是不科學的, let 和 const 皆不容許做用域內重複一個定義同名變量。
首先,絕對是不容許在let定義變量前使用這個變量的
console.log(a); // ReferenceError: a is not defined let a = 0; console.log(a); // 0 複製代碼
let聲明不會被提高到當前執行上下文的頂部,從該塊級做用域開始,到初始化位置,稱做「暫存死區」,因此在對於a的暫存死區中使用a會報「Reference」錯誤。
let聲明的變量擁有塊級做用域,在塊級做用域外是訪問不到該變量的,而var不一樣,在for循環中定義的變量能夠在外部訪問到,這會致使一些意想不到的bug。注意,大括號 {} 是塊級做用域,不是說函數做用域。
let a = 0; console.log(a); // 0 let a = 1; console.log(a); // SyntaxError: Identifier 'a' has already been declared 複製代碼
在 HTML 中, 全局做用域是針對 window 對象,var關鍵字定義的全局做用域變量屬於 window 對象,而let定義的不屬於。注意這是在瀏覽器環境下,若是是Node則無window對象。
var a = 0; console.log(window.a) // 0 let b = 1; console.log(window.b) // undefined 複製代碼
其實const和let很是很是相似,let該有的特性,const都有,不一樣就是,第一,const不容許「修改」變量,第二const必須初始化,而let不須要
const a = 0; a = 1; // TypeError: Assignment to constant variable 複製代碼
const b; // SyntaxError: Missing initializer in const declaration 複製代碼
不過const定義的不是真正意義上的常量,若是定義的是一個對象或者數組,實際上是能夠變化的,只不過不能將不一樣的數據類型分配給他,通常能夠記:const初始化定義後,不可以使用 = 賦值。
const a = {} a.b = 1; console.log(a) // { b: 1 } a = 1 // TypeError: Assignment to constant variable 複製代碼
本文使用 mdnice 排版