ES6 - let、const、var的区别

为了使JavaScript语言能够用来编写复杂的大型应用程序,成为企业级开发语言,ECMAScript 6.0(简称ES6)在标准中添加了不少新的特性。咱们将用几篇文章总结一下ES6标准中一些经常使用的新特性。本片文章主要讲解ES6中的letconst命令,并区分其与var命令的区别。同时欢迎你们随时指正错误、探讨交流。git

本文已同步至个人我的主页。欢迎访问查看更多内容!谢谢你们的关注和支持!github

let 与 var 的区别

1、let声明的变量只在其所在的块级做用于有效

所谓块级做用域是指:将多个代码语句封装在一块儿,一般是包含在一个大括号中,没有返回值。好比:web

if (true) {  // 块级做用域  }

for (let i = 0; i < 10; i++) {  // 块级做用域  }

while (true) {  // 块级做用域  }

switch (case) {  // 块级做用域  }
复制代码

以上例子,大括号({...})中造成的都属于块级做用域。数组

众所周知,在ES6以前,JavaScript中只有全局做用域和局部(函数)做用域,不存在块级做用域。并且也只能使用关键字var来声明变量。因此用var声明的变量要么是属于全局做用域的全局变量,要么就是属于局部(函数)做用域的局部变量。bash

在ES6标准中,添加了使用let声明变量的方式。使用let声明的变量只在块级做用域中有效,在其外层做用域访问时就会报错。函数

if (true) {
    // 这个用let声明的变量a,只在当前块级做用域中有效
    let a = 123;
    // 这个用var声明的变量b,在全局做用域中都有效
    var b = '123';

    console.log(a);     // 123
    console.log(b);     // '123'
}

console.log(a);     // 报错 —— ReferenceError: a is not defined.
console.log(b);     // '123'
复制代码

上面的例子中,由于变量a是使用let声明的,它只在其所在的块级做用域——if后面的大括号({...})之中有效,在块级做用域外层访问时就会报错。而用var声明的变量b,不受块级做用域的约束,能够跨块级做用域访问。这个例子中,变量b实际是属于全局做用域的全局变量。ui

那么,为何ES6中须要引入块级做用域的概念呢?为何要增长使用let来声明变量的方式呢?spa

由于,若是没有块级做用域会致使一些不合理的情形出现。指针

一、 内层变量可能会覆盖外层变量。code

var a = 'Global';

function inner() {
    if (true) {
        console.log(a);     // undefined
        var a = 'inner';
        /**
         * 以上两行代码至关于
         * var a;
         * console.log(a);
         * a = 'inner'; 
         * 再次使用var声明同名变量a,会覆盖全局变量a
         */
    }
}

inner();
复制代码

这个例子,当在函数inner内部if代码块内首先访问变量a时,却获得的是undefined。这是由于紧随其后var声明的同名变量a会变量提高并覆盖全局变量a。因此打印出a的值为undefined

二、计数的循环变量会泄露为全局变量

for (var i = 0; i < 10; i++) {
    // 一些循环操做
}

console.log(i);     // 10
复制代码

上面的例子,for循环中的循环变量按道理来讲应该只属于for循环体,循环结束就不能再访问。但实际这样用var声明的i,属于外层做用域中的变量,也就是说i泄露为全局变量。因此当执行到console.log(i)时,由于i通过循环已经增长到10,因此打印出i的值为10

2、let声明的变量不存在变量提高过程

var声明的变量,会在其做用域中发生变量提高的过程。变量会被提高到做用域顶部,JS默认给变量一个undefined值。在使用var声明一个变量前访问它,获得的值永远是undefined

可是,在ES6中使用let声明的变量,不存在变量提高过程。也就是说,不能在使用let声明任何一个变量前访问它,不然都会报错。

console.log(a);     // 报错——ReferenceError: a is not defined

let a = 'Hello World!'复制代码

3、let声明的变量存在“暂时性死区”

只要使用let声明了一个变量,那这个变量就“绑定”到了这个做用域(全局/局部/块级),该变量就再也不受外层做用域的影响。

ES6明确规定,若是区块中存在letconst命令,这个区块对这些命令声明的变量从一开始就造成了封闭做用域。凡是在声明以前就使用这些变量,就会报错。

总之,在代码块内,使用let命令声明变量以前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)。

let g = 'Global';

if (true) {
    g = 'Block';    // 报错——ReferenceError: g is not defined
    let g;
}
复制代码

上面的例子中,if代码块最顶部一直到let声明变量g以前,都是g的“暂时性死区”。在该范围内访问g都会报错。

4、let声明的变量不容许再次重复声明

使用var声明变量,能够屡次重复声明一个同名变量。最终变量的值为最后一次声明赋值的结果。

var a = 123;
var a = 'Hello World!';

console.log(a);     // 'Hello World!'
复制代码

可是,在同一做用域(全局/局部/块级)中不容许使用let重复声明变量。或者说不容许存在与用let声明的变量同名的变量。如下代码都会报错!

// 先var,后let
var a = 123;
// ...一些代码
let a = 'Hello World!';     // 报错——Uncaught SyntaxError: Identifier 'a' has already been declared

// 先let,后var
let b = 123;
// ...一些代码
var b = 'Hello World!';     // 报错——Uncaught SyntaxError: Identifier 'a' has already been declared

// 先let,再let
let c = 123;
// ...一些代码
let c = 'Hello World!';     // 报错——Uncaught SyntaxError: Identifier 'a' has already been declared
复制代码

5、let声明的全局变量不会做为window对象的一个属性

使用var声明的全局变量,会被JS自动添加在全局对象window上,做为该对象的一个属性。

var myVar = 'myName';

console.log(window.myVar);      // 'myName'
console.log(window.hasOwnProperty('myVar'));    // true
复制代码

可是,使用let声明的全局变量不会做为window对象的一个属性。

let yourVar = 'yourName';

console.log(window.yourVar);      // undefined
console.log(window.hasOwnProperty('yourVar'));    // false
复制代码

这个例子能够看出,let声明的全局变量yourVar,并无被添加到window对象上,没有做为window的一个属性。

let 与const 的区别

在ES6中,上述全部let所具备的特性,对于const来讲一样存在。但constletvar的区别在于const是用来声明常量的。

常量具备如下特色:

1、常量值不可修改

一个常量,一旦声明,任什么时候间、任何地点都不能修改它的值。

const PI = 3.1415926;

console.log(PI);    // 3.1415926

PI = 3; // 报错——Uncaught TypeError: Assignment to constant variable.
复制代码

2、常量在声明时必须必须当即初始化(赋初始值)

不能只声明一个常量名,但不对其进行初始化赋值。不然在声明常量时就会报错。

const PI;   // 报错——Uncaught SyntaxError: Missing initializer in const declaration

PI = 3.1415926;
复制代码

3、常量的值不可修改的实质(重要!!)

实际上,常量的值不变,是指常量指向的那个内存地址中所保存的数据不可更改。对于简单的数据类型(数值,字符串、布尔值),他们自己具体的值就保存在常量所指向的那个内存地址中,因此不能修改改简单类型的数据值。

可是,若是一个常量的值是一个引用类型值,那么常量所指向的内存地址中实际保存的是指向该引用类型值的一个指针(也就是引用类型值在内存中的地址)。因此const只能保证该引用类型地址不变,但该地址中的具体数据是能够变化的。

下面的例子,代码不会报错,能够正常运行!

// !!!常量OBJ中实际保存的是后面的对象在内存中的地址!!!
const OBJ = {};

/**
 * !!!!!!!!!!
 * 修改OBJ.prop1,实际只是修改了对象的属性,
 * 但并无改变该对象在内存中的地址,
 * 因此常量OBJ并无发生变化
 * !!!!!!!!!!
 */
OBJ.prop1 = 123;
OBJ.prop2 = 'Hello World!'

/**
 * !!!!!!!!!!
 * 下面这一行就会报错,
 * 由于此时OBJ指向了另外一个对象,OBJ中保存的地址发生了变化
 * !!!!!!!!!!
 */
OBJ = {};   // 报错——Uncaught TypeError: Assignment to constant variable.
复制代码

下面的例子和上面同理。

const ARR = [];
ARR.push('Hello');  // 可执行
ARR.length = 0;     // 可执行
ARR = ['Dave'];     // 报错,由于ARR从新指向了数组['Dave']所在的内存地址
复制代码
相关文章
相关标签/搜索