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对象就完全冻结了。