ES6

1. let和const命令

2018-01-02  本文已影响0人  羊烊羴

let命令

let是新增加的语法,该方法的用法和var是一样的,区别在于let声明的变量只能let所在的代码块内有效

{
    let name='Tom';
    var age=10;
}
console.log(name); //
console.log(age); //10

利用这一特性我们可以用来实现优化for循坏

var a=[];
for(let i=0;i<10;i++){
    a[i]=function(){
        return i;
    };
}
console.log(a[6]()); //6
//在之前如果我们使用var定义变量,那么a[6]()的结果会是10,因为var定义的是全局变量,在循环中定义的i是全局变量,每一个a[i]返回的i的值都会指向全局的i,而全局的i在循环结束后值为10,所以导致运行的时候最后的值都是循环后的最后一轮的值,而使用了let后,每一次循环的i都是新的变量,在javascript的引擎内部会记住上一轮循环的值,在初始化本轮的变量i时,就会在上一轮的基础上进行计算并对本次的计算记过进行赋值,使用let对i赋值相当于我们在es5中使用闭包存储每一次i变量值的操作

//es5
var a=[];
var loop=function (i) {
    a[i]=function () {
        return i
        }
    }
for(var i=0;i<10;i++){
    loop(i)
}
console.log(a[6]()); //6

//另外在for循环中使用let时需要注意,在循环处设置的变量那部分是属于父作用域,而循环体内部则是属于单独子作用域

for(let i=0;i<3;i++){

    let i='abc';

    console.log(i);
}
//在循环结束后会打印三次'abc',这说明循环内部的变量i和for循环定义的变量i不在同一个作用域,有各自单独的作用域,而如果我们使用var进行定义则会报错

let定义的变量不会发生变量提升的情况,也就是我们之前说的变量的预解析,let定义的语法一定要在let之后对变量进行引用,否则会报错,我们可以比较一下使用var和let定义变量的区别

console.log(name); //undefined
var name='tom';

console.log(age); //index.html:15 Uncaught ReferenceError: age is not defined
let age=18;

let定义变量会有一个暂时性死区的现象,简称TDZ,在ES6中明确规定,如果在块级作用域中存在let或const命令,从一开始就形成了封闭的作用域,凡是在let声明变量之前使用该变量是不合法的,会报错

var num=2;

if(true){
    num='hello';

    let num;//index.html:15 Uncaught ReferenceError: num is not defined
}

//也就是说在块级作用域中,在let定义的变量之前使用该变量都是报错,即使该变量已经在全局作用域中定义

还有一些其它情况的死区的现象,如下

function bar(x=y,y=2){
    return [x,y];
}
bar();//index.html:12 Uncaught ReferenceError: y is not defined

function bar(x=2,y=x){
    return [x,y]
}
console.log(bar());//[2,2]

//在第一个函数中,因为在参数x=y时,y还没有声明,属于死区
//在第二个函数中,参数y=x时,x已经赋值为2

var x=x; //undefined

let x=x; //Uncaught ReferenceError: y is not defined

//使用var声明变量时,var x=x; 可以理解为var x; x=undefined;
//而使用let时,由于不存在变量提升,所以改写法相当于在没有声明x时去使用x,所以会报错

let不允许在相同作用域内,重复声明同一个变量

{
    var i=1;
    let i=2
}  //Uncaught SyntaxError: Identifier 'i' has already been declared

{
    let i=1;
    let i=2;
} // Uncaught SyntaxError: Identifier 'i' has already been declared

function fn(arg){
    let arg;
} //Uncaught SyntaxError: Identifier 'arg' has already been declared

function fn(arg){
    {
        let arg;
    }
}//不报错

//这里我们可以看出,1.函数的参数和函数内部时属于一个作用域内 2.使用了let定义变量后,不允许再次重复声明,即使是var也不可以

ES6允许任意的块级作用域嵌套,外层的块级作用域无法获取内层的块级作用域内的变量,内层作用域可获取外层作用域定义的变量,内层可以定义与外层相同的变量

 {
    let name='xm';
    let age=18;
        {
            let name='Tom'
            console.log(age);
        }
    }

let命令可以形成块级作用域,只在声明变量的块级作用域内起作用,不可以对变量进行重复声明,不会进行变量名提升,存在暂死性区域,在let定义变量之前对该变量进行引用会报错,在块级作用域外不能获取let定义的变量

const命令

const声明一个只读的常量,一旦声明,常量的值就不能更改,同时也意味着一旦我们使用const声明常量,需要立刻赋值,不能保留到以后赋值

const x='Tom';
    
x='xm';//index.html:14 Uncaught TypeError: Assignment to constant variable.

const x;

console.log(x);//Uncaught SyntaxError: Missing initializer in const declaration

const同样不支持变量提升,只在声明所在的块级作用域内有效,也存在暂死性区域,只能在声明的位置后面使用,并且不可以重复声明

const本质

const实际上保证的并不是变量的值不得改动,而是变量指向的内存地址不能改动,那么我们都知道,对于简单类型的数据来说,值就保存在变量指向的内存地址中,因此地址等同于值,但对于复杂类型的数据来说,地址保存的只是一个指针,const只能保证这个指针是固定的,并不能保证指针指向的地址内的数据结构不变

const obj={};

obj.name='xm';  
console.log(obj.name);//xm

obj={};//Uncaught TypeError: Assignment to constant variable.

//我们为const定义的对象添加数据并不会报错,而且可以获取到添加的数据,但是如果想要为obj重新赋值会立即报错

const a=[];

a.push('hello');
a.length=0;

a=['hi'];//Uncaught TypeError: Assignment to constant variable.

//对于const定义的复杂类型,我们可以对其属性和数据进行添加或修改,但不能改变其地址

const用于声明一个不可变的变量,如果是简单类型的数据,那么该值不可以被修改,如果是复杂数据类型的话,可以对其属性进行修改,对数据进行添加,但是不能对其进行重新赋值操作,coust声明的变量不能被提升,只能在声明的块级作用域中使用,同样具有暂死性区域,不可以进行重复定义

在之前,我们使用var定义一个全局变量,那么该变量会被挂载到window(顶层对象)上,这也造成来了只要是全局属性,那么一定是顶级对象的属性,为了规避这个问题同时保持兼容性,在ES6中规定var和function定义的全局变量依旧是顶层对象的属性,但是由let,coust,class声明的全局变量,不属于顶层对象的属性,也就是说从ES6开始,顶层属性将会逐渐与全局变量脱钩

var a="a";
let b="b";
console.log(window.a); //a
console.log(window.b); //undefined
上一篇下一篇

猜你喜欢

热点阅读