ES6新增了一個let命令,用於聲明變量,與var命令用法相似,可是用let聲明的變量只在let命令所在的代碼塊有效。javascript
{ let name = "abc"; var age = 18 } console.log(name); console.log(age);
上面的代碼在代碼塊中,分別用let和var定義了兩個變量,在代碼塊外,能夠打印出用var聲明的age,可是打印不出用let聲明的name。這代表,用let聲明的變量只在其所在的代碼塊中有效。java
for循環如今就很適合使用let命令了。閉包
{ let arr = []; for (let i = 0, len = arr.length; i < len; i++) { } }
另外,使用let命令能夠解決以前的閉包問題。函數
var a = []; for (var i = 0; i < 10; i++) { a[i] = function () { console.log(i); }; } a[6](); // 10
上面代碼用var聲明瞭變量i後,是在全局範圍內都有效的。因此每一次循環,新的i都會覆蓋以前的舊值,致使最後輸出的是最後一個i的值。spa
在ES6之前,解決方式一般是採用閉包的方式。code
var a = []; for (var i = 0; i < 10; i++) { (function (i) { a[i] = function () { console.log(i); } })(i); } a[6](); // 6
如今有了let命令,能夠再也不使用閉包方式解決該問題,直接經過let聲明變量i就能夠達到該效果。orm
var a = []; for (let i = 0; i < 10; i++) { a[i] = function () { console.log(i); }; } a[6](); // 6
上面的代碼用let聲明瞭i變量,當前的i只在本輪循環有效,因此每一次循環的i都是一個新的變量,最後輸出的是6。對象
let命令聲明的變量不會像用var聲明的同樣,發生「變量提高」的現象。因此,變量必定要在聲明後使用。ip
console.log(name); // abc var name = 'abc'; console.log(name); // ReferenceError: can't access lexical declaration `name' before initialization let name = 'abc';
只要塊做用於中存在let命令聲明的變量,該變量就「綁定」在這個塊級做用域中,再也不受外部聲明的變量影響。作用域
var name = 'abc'; { name = 'jack'; let name; } // ReferenceError: can't access lexical declaration `name' before initialization
上面的代碼先聲明瞭name,又在塊級做用域中用let聲明瞭name,致使後者綁定了這個做用域,因此在let聲明name前,對name賦值會報錯。
let不容許在相同的做用域內重複聲明一個變量。
function test() { let a = 10; let a = 20; // SyntaxError: redeclaration of let a } function test1() { let a = 10; var a = 30; // SyntaxError: redeclaration of let a } function test2(arg) { let arg; // SyntaxError: redeclaration of formal parameter arg } function test3(arg) { { let arg; // ok } }
上面代碼中,前三個函數都報錯了,由於在同一個做用域下存在對let聲明的變量進行了重複用聲明,而test3函數沒有報錯,由於不在同一個做用域中。
const用來聲明常量。一旦聲明,其值就不能夠再改變。
const PI = 3.14; PI = 3; // TypeError: invalid assignment to const `PI'
const與let同樣,只在聲明所在的做用域有效,也一樣存在TDZ,只能在聲明後使用,也不能夠重複聲明。
對於對象類型的變量,其聲明不指向對象的數據,而是指向對象所在的地址。const命令只是保證變量名指向的地址不變,並不保證該地址下的數據不變。
const user = {}; user.name = 'jack'; console.log(user.name); // jack user = {}; // TypeError: invalid assignment to const `user'
上面的代碼中,常量user保存着一個對象地址,該對象自己是可變的,能夠添加name屬性,可是地址不可變,將user從新複製給一個地址會報錯。
若是想使const聲明的對象數據也不可變,可使用Object.freeze方法凍結對象。
const user = Object.freeze({ name: 'jack' }); console.log(user.name); // jack user.name = 'mark'; console.log(user.name); // jack
上面的代碼中,常量user所指向的對象被凍結,修改name屬性無效,可是若是name屬性指向的不是字符串,而是一個對象,該處理方式則無效。
let role = {name: 'admin'}; const user = Object.freeze({ name: 'jack', role: role }); console.log(user.name); // jack console.log(user.role.name); // admin user.name = 'mark'; user.role.name = 'qa'; console.log(user.name); // jack console.log(user.role.name); // qa
能夠看到,user的role屬性是個對象,而role的name屬性仍然能夠修改,想要解決這種狀況,須要將對象的屬性也凍結。
let role = {name: 'admin'}; const user = { name: 'jack', role: role }; let constantize = (obj) => { Object.freeze(obj); Object.keys(obj).forEach((key, value) => { if (typeof obj[key] === 'object') { constantize(obj[key]); } }); }; constantize(user); console.log(user.name); // jack console.log(user.role.name); // admin user.name = "mark"; user.role.name = "qa"; console.log(user.name); // jack console.log(user.role.name); // admin
上面代碼中,constantize是一個將對象完全凍結的函數,調用該函數後,則user對象就完全凍結了。