let和var一样是用来声明变量的。但是,它与var有很大的不同。
使用let声明的变量只在其所在的代码块内有效。
if (true) { var a = 1; }
console.log(a); //1
if (true) { let a = 1; }
console.log(a); 报错
在全局作用域中,使用let声明的变量不会是window对象的属性。
var age = 1;
console.log(window.age) //1
let age = 1;
console.log(window.age) //undefined
let与for循环
for (var i = 0; i < 10; i++) {
arr[i] = function () {
console.log(i)
}
}
arr[0]() //10
arr[9]() //10
由于变量i指向的是同一个,所以for循环结束后,数值变成了10,输出的也就是10
for (let i = 0; i < 10; i++) {
arr[i] = function () {
console.log(i)
}
}
arr[0]() //0
...
arr[9]() //9
当使用了let关键字后,每次循环都会重新创建i,所以输出的是0-9
使用let的时候,for的括号内为父作用域,花括号内子作用域,它们之间相互独立。
for (let i = 0; i < 10; i++) {
let i = "a";
console.log(i) //"a"
}
可以看到,两个变量i之间是独立的,互相不影响。
for (let i = 0; i < 10; i++) {
console.log(i) 报错
let i = "a";
console.log(i) //"a"
}
需要注意的是,在作用域内使用一个let声明的变量的时候,必须是在声明之后使用,否则一律报错!
所以可以看到,绝不像var那样,在变量声明之前使用变量会输出undefined,这种现象称之为变量提升。
而let则杜绝了这种现象,告诉我们先声明,后使用,才是好习惯!
console.log(a)//undefined
var a = 1;
console.log(a) //报错
let a = 1;
创建私有作用域
es5:
(function () {
var a = 1;
function fn () {}
})()
console.log(a) 报错
fn() 报错
es6:
{
let a = 1;
let fn = function () {};
}
console.log(a) 报错
fn() 报错
不过可惜的是es6的方式不能够有返回值。
使用const声明变量
const声明变量的最大特点是,声明的时候必须立即初始化,声明过后不可修改,否则报错!
const name = 1; //正确
const name; //错误
const name = 1;
name = 2; //错误
const与let声明的变量一样,只在其所在的代码块内有效,没有变量提升,必须先声明,后使用。在全局作用域中声明的变量不属于window对象的属性。
需要注意的是,对于引用类型的值,const只能保证其内存地址不变,因此在对象上进行一些修改是可行的。
const name = {
a : 1
}
name.a = 2;
console.log(name.a) //2
name = {} 错误
如果想要冻结对象,可以使用freeze方法。
const obj = Object.freeze({
name : 1
})
obj.name = 2 //无效 严格模式下将报错
但如果对象中包含有对象类型的值,那么这个对象内的对象是可变得。
const obj = Object.freeze({
name : 1,
arr : [1]
})
obj.arr.push(1)
console.log(obj.arr) //[1,1]
可以使用递归方法彻底冻结对象
let frreezeObj = (obj) => {
//冻结对象
Object.freeze(obj);
//object.keys方法返回obj中所有可枚举的属性的数组,然后对数组进行遍历
Object.keys(obj).forEach((key, i) => {
//如果是引用类型的话
if (typeof obj[key] === 'object') {
//调用自身,将对象冻结
frreezeObj(obj[key]);
}
})
}
let obj = {
arr : [1]
}
frreezeObj(obj);
obj.arr.push(1) //报错:对象不可拓展