为了使JavaScript语言能够用来编写复杂的大型应用程序,成为企业级开发语言,
ECMAScript 6.0
(简称ES6
)在标准中添加了不少新的特性。咱们将用几篇文章总结一下ES6标准中一些经常使用的新特性。本片文章主要讲解ES6中的let
、const
命令,并区分其与var
命令的区别。同时欢迎你们随时指正错误、探讨交流。git
本文已同步至个人我的主页。欢迎访问查看更多内容!谢谢你们的关注和支持!github
所谓块级做用域是指:将多个代码语句封装在一块儿,一般是包含在一个大括号中,没有返回值。好比: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
。
用var
声明的变量,会在其做用域中发生变量提高
的过程。变量会被提高到做用域顶部,JS默认给变量一个undefined
值。在使用var
声明一个变量前访问它,获得的值永远是undefined
。
可是,在ES6中使用let
声明的变量,不存在变量提高
过程。也就是说,不能在使用let
声明任何一个变量前访问它,不然都会报错。
console.log(a); // 报错——ReferenceError: a is not defined
let a = 'Hello World!';
复制代码
只要使用let
声明了一个变量,那这个变量就“绑定”到了这个做用域(全局/局部/块级),该变量就再也不受外层做用域的影响。
ES6明确规定,若是区块中存在let
和const
命令,这个区块对这些命令声明的变量从一开始就造成了封闭做用域。凡是在声明以前就使用这些变量,就会报错。
总之,在代码块内,使用let
命令声明变量以前,该变量都是不可用的。这在语法上,称为“暂时性死区”
(temporal dead zone,简称 TDZ)。
let g = 'Global';
if (true) {
g = 'Block'; // 报错——ReferenceError: g is not defined
let g;
}
复制代码
上面的例子中,if
代码块最顶部一直到let
声明变量g
以前,都是g
的“暂时性死区”。在该范围内访问g
都会报错。
使用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
复制代码
使用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
的一个属性。
在ES6中,上述全部let
所具备的特性,对于const
来讲一样存在。但const
与let
、var
的区别在于const
是用来声明常量的。
常量具备如下特色:
一个常量,一旦声明,任什么时候间、任何地点都不能修改它的值。
const PI = 3.1415926;
console.log(PI); // 3.1415926
PI = 3; // 报错——Uncaught TypeError: Assignment to constant variable.
复制代码
不能只声明一个常量名,但不对其进行初始化赋值。不然在声明常量时就会报错。
const PI; // 报错——Uncaught SyntaxError: Missing initializer in const declaration
PI = 3.1415926;
复制代码
实际上,常量的值不变,是指常量指向的那个内存地址中所保存的数据不可更改。对于简单的数据类型(数值,字符串、布尔值),他们自己具体的值就保存在常量所指向的那个内存地址中,因此不能修改改简单类型的数据值。
可是,若是一个常量的值是一个引用类型值,那么常量所指向的内存地址中实际保存的是指向该引用类型值的一个指针(也就是引用类型值在内存中的地址)。因此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']所在的内存地址
复制代码