ES6学习笔记1——let和const的认识

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/cc18868876837/article/details/79957030

一. let

    let和var一样用于定义变量,和var的区别在于:let是块级的,而var是函数级的。在以前的JavaScript中,只有两种作用域,一种是全局作用域,另一种是函数作用域,ES6通过let关键字增加了块级作用域

(1)let的块级作用域

{
var a = 1;
let b = 2;
}
console.log(a, b);

    此时将会报错:Uncaught ReferenceError: b is not defined,因为变量b无法在块级作用域以外被访问。

{
var a = 1;
let b = 2;
{
console.log(a, b);
}
}

    此时输出:1 2 。子级是可以访问到父块级作用域中的变量。

for(var i = 0; i < 5; i++) {}
console.log(i);

    此时输出:5 。若将var改为let,也会报错:Uncaught ReferenceError: i is not defined。因为let声明的i只在for循环中起作用。

(2)let不存在变量的提升

function f() {
console.log(a);
var a = 1;
}
f();

    定义一个函数f()并执行,结果:undefined,此时并没有报错,因为var声明有变量提升的作用,在var声明的变量a之前使用到a,因为此时a还未赋值,所以值为undefined的。以上的代码相当于如下:

function f() {
var a;
console.log(a);
a = 1;
}
f();

    若将var改为let:

function f() {
console.log(a);
let a = 1;
}
f();

    则报错:Uncaught ReferenceError: a is not defined,说明let不能使变量的声明提升

    变量临时失效现象。如下:

var a = 1;
function f() {
console.log(a);
}
f();

    此时f()函数执行时肯定是可以访问到a变量,结果:1。若代码为如下:

var a = 1;
function f() {
console.log(a);
let a = 1;
}
f();

    此时将报错:Uncaught ReferenceError: a is not defined 。因为在f()函数的块级作用域内,此时已经有let定义的变量a,所以外面var定义的全局变量a将失效,而console.log是在声明变量a之前调用,并且let声明没有变量提升的功能,所以此时a还未定义,所以会报错。ES6明确规定,如果在区块中存在let和const命令,则这个区块对这些命令声明的变量从一开始就形成封闭作用域,外面的同名变量不起作用,并且只要在声明之前使用这些变量,就会报错,这在语法上称为“暂时性死区”(Temporal Dead Zone,简称TDZ),如下所示:

var a = 1;
function f() {
//TDZ开始
a = 2; //Uncaught ReferenceError: a is not defined
console.log(a); //Uncaught ReferenceError: a is not defined
let a = 1; //TDZ结束
console.log(a); //1
}
f();
    TDZ的存在以及变量不可提升的原因是为了减少运行时错误,防止变量在声明前就使用。

(3)let不允许重复声明变量

{
var a = 1;
var a = 2;
console.log(a);
}

    用var声明两次变量a,并且赋予不同的值,结果输出为:2 。说明了var声明变量允许重复声明,并且值为最后一次赋予的值。若将var改为let:

{
let a = 1;
let a = 2;
console.log(a);
}

{
let a = 1;
var a = 2;
console.log(a);
}

    以上两个都报错:Uncaught SyntaxError: Identifier 'a' has already been declared 。说明let不允许在相同作用域中重复声明变量。不过以下这种使用没有影响:

{//作用域1开始
let a = 1;
console.log(a); //1
//作用域1结束
{//作用域2开始
let a = 2;
console.log(a); //2
}//作用域2结束
//作用域1开始
console.log(a); //1
}////作用域1结束
    从以上可以看出,let的这种块级作用域以及不允许重复声明的特性,可以用来替代立即执行匿名函数的作用,如下:
var config = (function(){
var config = [];
config.push(1);
config.push(2);
config.push(3);
})();

    可以使用let的写法来替代:

let config;
{
config = [];
config.push(1);
config.push(2);
config.push(3);
}

(4)实际的例子

    var arr = [];
    function f() {
     for(var i = 0; i < 5; i++){
     arr.push(function () {
     console.log(i);
     })
     }
    }
    f();
    arr[2]();

    以上代码想实现的功能是:arr数组中的每个元素保存一个函数,调用指定下标的函数时能输出对应的下标。然而并不像预想的那样会输出2,结果输出为5,arr[1]()....,arr[4]()的输出结果都是5。因为for循环中var定义的i的作用域为整个f()函数,每一次循环,新的i值都会覆盖旧值。执行完函数f()后,i最终为5,而arr中函数使用到的始终是这个i,所以最终执行都输出为5 。

    要想实现预想的功能,以前的做法是通过立即执行匿名函数(IIFE)来实现:

    var arr = [];
    function f() {
     for(var i = 0; i < 5; i++){
     arr.push((function (i) {
     return function () {
     console.log(i);
     }
     })(i));
     }
    }
    f();
    arr[2]();

    通过立即执行匿名函数来保存对应的i值。

    现在单地将for循环中的var改为let即可:

    var arr = [];
    function f() {
     for(let i = 0; i < 5; i++){
     arr.push(function () {
     console.log(i);
     })
     }
    }
    f();
    arr[2]();

    变量i是let声明的,当前的i只在本轮循环中有效。所以每一次循环的i其实都是一个新的变量。

    另外补充一点,在for循环中,其定义循环条件的部分和循环体部分并不是在同一个作用域的,循环条件部分是循环体的父级域,如下例子:

for(let i = 0; i < 2; i++) {
let i = "CC";
console.log(i);
}
    这段代码能正常执行,输出两次“CC”。

二. const

   const除了和let的特性都一样以外,唯一的区别是const定义的常量的值不可更改。这里的常量不可更改是指物理内存地址不可更改,而地址中的内容是可以更改的,这就要求const声明时必须初始化。如下:

const a = 1;
a = 2;
console.log(a);

    以上代码将报错:Uncaught TypeError: Assignment to constant variable. 常量定义以后是不可以再重新赋值的,而常量对象内的变量是重新赋值的,如下:

const a = {
name: 'Bob'
};
a.name = 'Chen cong';
console.log(a.name);

    此时输出为:Chen cong 。这里改变的是a对象中的内容,而指向对象a的地址并未改变,故可以成功运行。若将a指向另一个对象的话将报错。对于复类型(如数组,对象等)的变量,变量名不指向数据,而是指向数据所在的地址,const命令只保证变量名指向的地址不变,并不保证该地址的数据不变。

    备注:若想对象中的内容也不可更改的话,那么可以使Object.freeze()方法来冻结对象。除了将对象本身冻结,对象的属性也应该冻结,如下函数为将一个对象彻底冻结

var constantize = function(obj) {
Object.freeze(obj);
Object.keys(obj).forEach(function(key, value) {
if(typeof obj[key] === 'object') {
constantize(obj[key]);
}
})
}

三. 其他:

    let、const、class命令声明的全局变量不属于全局对象的属性。全局变量有两种,在浏览器中指的是window对象,在Node.js中指的是global对象。ES5中,定义全局变量与定义全局对象的属性是等价的。如下:

//ES5
var a = 1;
console.log(a); //1
console.log(window.a); //1
//ES6
let a = 1;
console.log(a); //1
console.log(window.a); //undefined

阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页