1. let声明的变量只在它所在的代码块有效
{ let a = 10; 3 var b = 1; 4 } 5 a // ReferenceError: a is not defined. 6 b // 1
for(var i=0; i<10; i++){ a[i]=function(){ console.log(i); } } a[6](); //10 比较 var a=[]; for(let i=0;i<10;i++){ a[i]=function(){ console.log(i); } } a[6]() let 生成6个作用域 每个作用域有各自的i 和全局作用域,,,而var 声明的 只有全局作用域和各自的函数作用域
2.var“变量提升”现象是变量在申明前使用,会返回underfined。let
不像那样会发生“变量提升”现象。所以变量一定要在声明后使用,否则报错。
3.在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称TDZ)。
4 .暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。
function test(x = y, y = 2) { return [x, y]; } test(); // 报错
function test(x = 2, y = x) { return [x, y]; } test(); // [2, 2]
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
下面是一个将对象彻底冻结的函数
var constantize = (obj) => { Object.freeze(obj); Object.keys(obj).forEach( (key, value) => { if ( typeof obj[key] === 'object' ) { constantize( obj[key] ); } }); };
const
命令只是保证变量名指向的地址不变,并不保证该地址的数据不变,所以将一个对象声明为常量必须非常小心。
const foo = {}; foo.prop = 123; foo.prop // 123 foo = {}; // TypeError: "foo" is read-only
考虑到环境导致的行为差异太大,应该避免在块级作用域内声明函数。如果确实需要,也应该写成函数表达式,而不是函数声明语句。
// 函数声明语句 { let a = 'secret'; function f() { return a; } } // 函数表达式 { let a = 'secret'; let f = function () { return a; }; }
ES5只有两种声明变量的方法:var
命令和function
命令。ES6除了添加let
和const
命令,后面章节还会提到,另外两种声明变量的方法:import
命令和class
命令。所以,ES6一共有6种声明变量的方法。
补充整理:
对于let 和 const 最大的好处就是避免了可能的运行时错误, 不过也有直观的好处:
- 用块(Blocks)替换立即执行函数(IIFEs)
- 定义循环变量不会外泄
- 循环定义函数可以不用闭包了
- 可以放心的在 if 等条件中定义函数