-
let命令
用来声明变量,它的用法类似var,但是let命令声明的变量只在所在的代码块中有效。{ var a = 1; let b = 2; } console.log(a); // 1 console.log(b); // b is not defined b未定义
这就说明let定义的变量只在对应的代码块中有效。
同样的下面是个for循环for(let i = 0; i < 5; i++){ // ... } console.log(i);// i is not defined i未定义
i只在循环体内有效。
阮一峰老师的文章中有一个例子我们看一下并且分析一下var a = []; for (let i = 0; i < 10; i++) { a[i] = function () { console.log(i); }; } a[6](); // 6
老师给我们的分析是这样子的:
上面代码中,变量i是let声明的,当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的变量,所以最后输出的是6。你可能会问,如果每一轮循环的变量i都是重新声明的,那它怎么知道上一轮循环的值,从而计算出本轮循环的值?这是因为 JavaScript 引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算。
如果还不理解的话我们可以再看一个例子:var a = []; for (let i = 0; i < 10; i++) { let val = i; a[i] = function () { console.log(val); }; } a[6](); // 6
这时候你就会理解原因了,就相当于for循环中的执行体,每次的执行都位于一个单独的代码块中。这个val是我们显示定义的一个副本,当我们执行a[6]的时候,他会找到a[6]保存的函数的执行环境下去找这个val,数组a中的每个元素都位于一个单独的代码块中互不影响。那么我们可以得出一个结论:js引擎会为我们for循环中每次循环的代码块保存一个副本。
Nicholas C. Zakas
的《深入理解ES6》
中也有相关解释。
不存在变量提升console.log(a);// undefined console.log(b);// b is not defined var a = 1; let b = 2;
暂时性死区
只要块级作用域中存在let命令,那么let声明的变量就会绑定这个作用域不受外部影响。var temp = 'hello'; if(true){ console.log(temp);// temp is not defined let temp; } 原因就是let绑定了块级作用域并且let定义的变量不会提升,就是这个地盘跟我同名的都得死。 隐蔽的死区 function test(x = y, y = 2) { return [x, y]; } test();// 报错
原因是:当执行test()的时候会先将y的值赋给x作为初始值,但此时y不存在,所以报错。
function test(x = 2, y = x) { return [x, y]; } test();// [2, 2]
这样就没问题了。
总之,暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。
不允许重复声明
let不允许在同一作用域声明两个相同的变量。let a; var a;//报错 a已经被声明了
function fun(params) { let params; } func();//报错 params已经被声明了 function test(params) { {//内部代码块 let params; } } test();//正常执行 执行函数内部的代码块被其中的那个变量占用了而已
块级作用域
先看一下es5,es5只有全局作用域和函数作用域。var name = 'Jason'; function test() { console.log(name); if(false) { var name = 'Nico'; } } ==>相当于 var name = 'Jason'; function test() { var name;//变量提升 console.log(name); if(false) { name = 'Nico'; } } 所以执行test()会打印undefined
还有就是for循环的时候i的定义被提前
for(var i = 0; i < 3; i++) { console.log(i); } console.log(i); 依次打印0 1 2 3
原因是i定义被提前,如下:
var i; for(i = 0; i < 3; i++) { console.log(i); } console.log(i);
es6出现了块级作用域
function test() { let n = 0; if(true) { let n = 1; } console.log(n); } test();// 0
外层的块级作用域不受内层的控制。
{ { let a = 1; } console.log(a);//报错 a未被定义 }
外层作用域无法访问内层作用域定义的变量
{ let a = 0; { let a = 1; } }
内层作用域可以命名外层已经命名的变量。
值得提醒的是函数在块级作用域中的声明function test() { console.log('outside'); } (function() { if(false) { function test() { console.log('inside'); } } console.log(test()); })();
ES5会打印inside字符串,ES6却会报test is not a function的错。
是不是很诧异,为什么ES6没有打印outside呢?原因是:ES6中允许块级作用域中声明函数,但是函数声明会类似于var提升到全局作用域或者函数作用域头部,同时函数声明会提升到块级作用域头部,所以上面的代码就相当于
function test() { console.log('outside'); } (function() { var test = undefined; if(false) { function test() { console.log('inside'); } } console.log(test()); })(); 所以才会报test is not a function的错误。
所以尽量避免块级作用域中声明函数,如果有必要的话可以使用函数表达式。
function test() { console.log('outside'); } (function() { if(false) { let test = function() { console.log('inside'); } } console.log(test());// outside })();
-
const命令
用于定义常量,一旦定义必须立刻初始化不然报错,定义后无法改变值,改变也会报错。const USER_NAME;//报错
const USER_ID = '410100'; USER_ID = 2;//报错
const的作用域与let命令相同:只在声明所在的块级作用域内有效
const命令声明的常量也是不提升,同样存在暂时性死区,只能在声明的位置后面使用。
const声明的常量,也与let一样不可重复声明。
const其实保证的是变量存储的内存地址不得改动,对于简单的数据类型(数值,布尔,字符串),值就存在内存地址指向的位置,就等同于常量。但是对于数组、对象保存的就是一个指针,只能保证这个指针不被改变。对于内部的数据结构的变化是无法控制的。const ARR = []; ARR.push(1);// 可以执行 ARR.length = 0;// 可以执行 ARR = [];//报错
const OBJ = {}; OBJ.name = 'JASON';// 可以执行 OBJ = {};//报错
ES6 声明变量的六种方法:
var
function
let
const
import
class
顶层对象:浏览器中指window Node中指global
ES5的时候全局变量的赋值与顶层变量的赋值是同一件事。
ES6改变了这一点除了var
与function
还保持原来的方式,新的声明变量的方法不再与顶层变量挂钩。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。