ES6初体验——(1)let和const命令
前言:
参考书籍:ECMAScript 6 入门 链接: http://es6.ruanyifeng.com/ 阮一峰老师,书中示例详尽,此处只做简单梳理和理解,仅作督促自己学习使用
1、let命令,用来声明变量。用法类似于 var
,但是所声明的变量,只在let
命令所在的代码块内有效。而ES5中,是没有块级作用域的概念的。
var a = []; for (var i = 0; i < 10; i++) {//i由var定义,属于全局变量,i只定义了一次,每次循环,i的值递增,for循环执行完毕,i的值变成10,所以输出10 a[i] = function () { console.log(i); }; } a[6](); // 10
var a = []; for (let i = 0; i < 10; i++) {//i由let定义,只在当轮循环有效,即每一轮循环的i都是重新声明的, a[i] = function () { //由于JavaScript 引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算 console.log(i); }; } a[6](); // 6 a[3](); // 3
for
循环还有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。
for (let i = 0; i < 3; i++) { let i = 'abc'; console.log(i); } // abc // abc // abc
var
命令会发生”变量提升“现象,即变量可以在声明之前使用,值为undefined,(这是因为预解析的缘故)。但是由let声明的变量则不存在变量提升现象,即不被预解析,它要求变量必须在声明语句之后才可以使用,否则会抛出错误。
console.log(foo); // Uncaught ReferenceError: foo is not defined 始终没有声明过就直接使用会报错
console.log(foo); // 输出undefined 先使用再声明,输出undefined(由var定义的变量) var foo = 2;
foo = 2; console.log(foo); //2 此处没有报错,反而输出了2,是因为此处的foo没有由var声明,则被默认为window下的一个属性 console.log(window.foo);//2
暂时性死区:只要块级作用域内存在let
命令(不管它的位置在前在后),它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响(外部是否定义过这个变量已经和这个区域无关了)。一旦在这个区域内,let声明不是在最前面,即先使用后才由let声明,则会报错。
if (true) { // TDZ开始 tmp = 'abc'; // ReferenceError console.log(tmp); // ReferenceError //事实上,在代码执行的时候,如果报错了,会阻塞后面的语句执行,所以,把这两句注释掉,后面的语句才会正常执行 let tmp; // TDZ结束 console.log(tmp); // undefined tmp = 123; console.log(tmp); // 123 }
let
不允许在相同作用域内,重复声明同一个变量。因此,不能在函数内部重新声明参数。
2、块级作用域
ES5 只有全局作用域和函数作用域,没有块级作用域,这带来很多不合理的场景。
第一种场景,内层变量可能会覆盖外层变量。
第二种场景,用来计数的循环变量泄露为全局变量,循环变量i
只用来控制循环,但是循环结束后,它并没有消失,泄露成了全局变量。
ES6 允许块级作用域的任意嵌套。
外层作用域无法读取内层作用域的变量。
内层作用域可以定义外层作用域的同名变量。
块级作用域的出现,实际上使得获得广泛应用的立即执行函数表达式(IIFE)(即匿名函数自执行)不再必要了。(原意是为了不定义全局变量,所以放在一个匿名函数自执行里面)。
// IIFE 写法 (function () { var tmp = ...; ... }()); // 块级作用域写法 { let tmp = ...; ... }
3、const命令
const
声明一个只读的常量,在声明时,要立即初始化该变量,即给该变量赋值,否则会报错。由const声明的变量,一旦声明,其值就不能改变。
在刚刚探讨的其他方面,const与let是相同的。
本质:const
实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指针。如果真的想将对象冻结,应该使用Object.freeze
方法。
const foo = {}; // 为 foo 添加一个属性,可以成功 foo.prop = 123; foo.prop // 123 // 将 foo 指向另一个对象,就会报错 foo = {}; // TypeError: "foo" is read-only
4、顶层对象的属性
顶层对象,在浏览器环境指的是window
对象,在 Node 指的是global
对象。ES5 之中,顶层对象的属性与全局变量是等价的。ES6 为了改变这一点,一方面规定,为了保持兼容性,var
命令和function
命令声明的全局变量,依旧是顶层对象的属性;另一方面规定,let
命令、const
命令、class
命令声明的全局变量,不属于顶层对象的属性。也就是说,从 ES6 开始,全局变量将逐步与顶层对象的属性脱钩。
var a = 1; // 如果在 Node 的 REPL 环境,可以写成 global.a // 或者采用通用方法,写成 this.a window.a // 1 let b = 1; window.b // undefined