ES6新特性总结(1)let及const

1 let和const出现的原因

let和const是ES6新增的两种新的声明格式,用于补全ES5标准中var声明变量的不足。

var声明变量的不足:在JavaScript中用"var"来声明变量会出现变量提升的情况,即通过"var"声明的变量,系统都会把声明隐式的升至顶部,这样的特性往往会让刚接触JavaScript及习惯其他语言的开发人员不适应,导致程序出现问题。

例如,在下面的代码中,首先输出a,然后再用var定义a,结果并不会报错,而是输出undefined:

console.log(a); //输出undefined
var a = 9;

这是因为var声明的变量会提升到全局进行预解析,因此预解析的结果为:

var a; //undefined
console.log(a);
a = 9;

但是使用let或者const进行声明变量就不会出现这样的问题,而是会直接报错:

console.log(a); //程序报错
let a = 9;

在这里插入图片描述

console.log(a); //程序报错
const a = 9;

在这里插入图片描述
因此let和const补全了ES5标准中声明变量的不足。

2 作用域

ES5中定义作用域有两种:全局作用域和函数作用域。ES6中新增了块级作用域,用"{ }"表示。

块级作用域用于声明作用域之外无法访问的变量,主要有两种:
(1)函数内部块级作用域

function test() {
    let a = 20;
    console.log(a);
}
test(); //调用test()方法输出a=20
console.log(a); //访问不到test()函数内的a,因此会报错

(2)在字符{ }之间的区域

{
    let a = 10;
}
console.log(a); //访问不到块级作用域中的a,程序报错

再看一个例子,在下面的循环中,当sum定义在for循环内部时,是无法输出sum的值的,程序会报错:

for (let i = 0; i < 100; i++) {
    let sum = 0;
    sum = sum + i;
}
console.log(sum);//sum is not defined

因此我们需要将sum定义到for循环的外部,才能有效地执行该循环。

3 const命令

使用const声明的是常量,常量的值不能通过重新赋值来改变,并且不能重新声明,所以每次通过const来声明的常量必须进行初始化

与其他语言不同,const在使用过程中如果声明的是对象,需要注意是可以修改对象的属性值,但不允许修改已经声明的对象

const obj = {
    name: "橘猫吃不胖",
    age: 2
}
obj.name = "张三"; //修改name属性值为张三
console.log(obj); //输出该对象

输出结果为:{ name: ‘张三’, age: 2 }

从上面的结果可以看出,在定义obj对象时,我为name属性定义的属性值是“橘猫吃不胖”,但是通过修改之后,name属性值成为了“张三”,因此const声明的对象可以修改对象的属性值

但是如果要修改已经声明的对象,程序就会报错:对常数变量赋值

const obj = {
    name: "橘猫吃不胖",
    age: 2
}
obj = {}; //将该对象设置为空
console.log(obj);//报错:TypeError: Assignment to constant variable.

如果想让对象属性不能修改,可以借助Object.freeze函数来冻结对象。

const obj = {
    name: "橘猫吃不胖",
    age: 2
}
Object.freeze(obj); //冻结该对象
obj.name = "张三"; //修改对象的name属性值
console.log(obj); //输出该对象

结果仍然是:{ name: ‘橘猫吃不胖’, age: 2 }

通过Object.freeze冻结对象需要注意的是:不能冻结多层对象

const obj = {
    name: "橘猫吃不胖",
    age: 2,
    family: {
        name: "张三",
        age: 20
    }
}
Object.freeze(obj); //冻结该对象
obj.family.name = "李四"; //修改obj对象中的family对象中的name属性
console.log(obj); //输出该对象

输出结果为:{ name: ‘橘猫吃不胖’, age: 2, family: { name: ‘李四’, age: 20 } }

解决多层冻结问题可以通过封装一个deepFreeze函数来实现:

function deepFreeze(obj) {
    Object.freeze(obj); //冻结输入的对象
    for (let key in obj) { //key代表了obj对象的所有属性,for...in循环会遍历该对象的所有属性
        //hasOwnProperty()判断对象自身属性是否含有指定的属性
        //如果对象中有一个属性,并且这个属性的类型是object,那么就冻结该属性
        if (obj.hasOwnProperty(key) && typeof obj[key] === "object") {
            deepFreeze(obj[key]);
        }
    }
}

我们为其设计一个复杂的对象,如下所示,对象相互嵌套构成了obj对象:

const obj = {
    name: "张三",
    age: 2,
    family: {
        father: {
            address: "CSDN",
            gender: "男"
        },
        mother: {
            name: "王五",
            age: 26
        }
    }
}

然后尝试修改一下father对象中address属性的值:

deepFreeze(obj); //调用deepFreeze()函数,冻结obj对象
obj.family.father.address = "AAA"; //修改address属性值为“AAA”
console.log(obj); //输出修改之后的obj对象

得到的结果为:
{
name: ‘张三’,
age: 2,
family: {
father: { address: ‘CSDN’, gender: ‘男’ },
mother: { name: ‘王五’, age: 26 }
}
}

可以看出address属性值并未被修改,冻结成功!

4 临时死区

let和const都是块级标识符,所以let和const都是在当前代码块内有效,常量不存在变量提升的情况。但是通过let和const声明的常量,会放在临时死区(temporal dead zone)。

{
    console.log(typeof a); //ReferenceError: Cannot access 'a' before initialization
    let a = 10;
}

即使通过安全的typeof操作符也会报错,原因是JavaScript引擎在扫描代码变量时,要么会把变量提升至顶部,要么会把变量放在临时死区。这里通过let声明的"a"变量会被放在临时死区,所以在声明之前打印就会报错。

5 循环中let和const的使用

在ES5标准中,for循环中的循环变量都是通过var来声明的,由于var没有独立的作用域,导致在循环中创建函数时会出现结果和思路不一致的情况。

let funArr = []; //该数组中存放的是函数
for (var i = 0; i < 5; i++) { //循环的次数
    funArr.push(function () { //添加函数
        console.log(i); //输出i
    })
}
funArr.forEach(item => {
    item();
})

这段代码意味着,每循环一次,就在fuynArr数组中添加一个输出循环的次数i的函数。循环结果不是预想的0,1,2,3,4,而是5个5,这是因为var声明在循环中作用域共用,并且会把i保存在全局作用域中。

要解决循环中保存函数的问题,可以利用闭包创建独立的作用域,代码如下:

let funArr = [];//该数组中存放的是函数
for (var i = 0; i < 5; i++) {
    (
        function (i) {
            funArr.push(function () {
                console.log(i);
            });
        }
    )(i);
}
funArr.forEach(item => {
    item(); //输出结果为0,1,2,3,4
})

for循环中的函数被用()括起来,而且()后还有一个(i),代表了函数在定义的同时就被调用了,因此for循环中的函数每一次循环都调用,成功在最后结果中输出0,1,2,3,4,这样通过自执行函数就可以解决循环中创建函数的问题。

但利用ES6中let和const提供的快级作用域可以让代码更简洁:

let funArr = [];
for (let i = 0; i < 5; i++) {
    funArr.push(function () {
        console.log(i);
    })
}
funArr.forEach(item => {
    item(); //输出结果为0,1,2,3,4
})

在for…in或for…of循环中使用const时,方法与let一致。

使用for…in遍历对象:

let obj = {
    name: "张三",
    age: 20
}
for (const i in obj) { //使用for...in遍历对象
    console.log(i); //输出属性name、age
    console.log(obj[i]); //输出属性值张三、20
}

使用for…of遍历数组:

let arr = ["张三", "李四", "王五"];
for (const i of arr) {
    console.log(i); //输出张三、李四、王五
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值