ES6新特性总结let及const

Posted 橘猫吃不胖~

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ES6新特性总结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); //输出张三、李四、王五

以上是关于ES6新特性总结let及const的主要内容,如果未能解决你的问题,请参考以下文章

ES6新特性之 let const

总结let 与 const新特性

ES6 新特性知识点总结

ES6新特性:let和const

前端知识总结--ES6新特性

ES6新特性1:let 和 const 命令