目录
var
- 声明一个变量,可选初始化一个值
- 会预解析
- 同一作用域可以重复声明变量
- 在函数作用域内有效
let
- 声明一个块作用域的局部变量,可选初始化一个值
- 不会预解析
- 同一作用域不可重复声明变量
- 在块级作用域内有效
- 存在暂时性死域(当一个区域存在let声明时,这个区域就形成了一个封闭的作用域,在let声明前使用这个变量就会报错,也就是只能先声明再使用,这种语法也称为暂时性死域。)
{
let a = 1;
{
console.log(a);//报错
let a =2;
}
}
const
- 声明一个块作用域的只读属性
- 声明后必须初始化赋值,否则会报错
const a=123;
- 不会预解析
console.log(a);
const a=10; //a is not defined
- 不可以重复声明
const a=1;
const a=2; //Identifier 'a' has already been declared
- 简单类型的值不可以改变,相当于常量;复杂类型的值可以改变,相当于保存了内存地址,指向的保存数据的地址不变,但是数据结构变不变就不可以保证了
//复杂对象有数组和对象
const Obj={};
Obj.name=123;
console.log(Obj); //输出:{name:123}
- 全局或块级作用域
const a=1;
window.οnlοad=function f1(){
const b=2;
console.log(a); //1
console.log(b); //2
}
console.log(b); //b is not defined
块级作用域:
产生的原因:避免在for循环中的变量泄露
//for循环中的变量泄露
for(let i=0;i<3;i++){
console.log(i);
}
console.log(i); // 0 1 2
for(let i=0;i<3;i++){
console.log(i);
}
console.log(i); //i is not defined
for循环的块级作用域
结论1:在for循环中使用let的情况下,由于块级作用域的影响,导致每次迭代过程中的 i 都是独立的存在。
结论2:既然说每次迭代的i都是独立的存在,那i自增又是怎么知道上次迭代的i是多少?这里通过ES6提到的,我们知道是js引擎底层进行了记忆
原理:
这是在for语句中的var与let的差异:
for (let x...)的循环在每次迭代时都为x创建新的绑定
以下用代码直接看会比较容易的理解。这个改进主要是为了要解决在for语句中的闭包结构的问题。
原来的使用var
的代码,与去糖(desugar)后来看它在执行时是这样的模拟代码:
//原来代码
for (var i = 0; i < 10; i++) { setTimeout(()=>console.log("i:",i), 1000); }
// 不需要加区块符,因为区块也不会影响
var i;
i = 0;
if (i < 10)
setTimeout(()=>console.log("i:",i), 1000);
i++;
if (i < 10)
setTimeout(()=>console.log("i:",i), 1000);
i++;
//...
而使用了let后,会有块级作用域的影响,原来的代码与执行时的去糖模拟代码如下:
// 原来代码
for (let i = 0; i < 10; i++) { setTimeout(()=>console.log("i:",i), 1000); }
// 用区块符区分每次循环的语句
// 每次for语句开始,i指定为一个全域刻度__status,这只是方便说明而已
// __status会记录for语句i最后的值
{ let i;
i = 0;
__status = {i};
}
{ let {i} = __status;
if (i < 10)
setTimeout(()=>console.log("i:",i), 1000);
__status = {i};
}
{ let {i} = __status;
i++;
if (i < 10)
setTimeout(()=>console.log("i:",i), 1000);
__status = {i};
}
//...
为何可以这样模拟?因为在ES标准中,有一段是关于CreatePerIterationEnvironment,也就是for语句每次循环所要建立环境的步骤,里面有提及有关词法环境的相关步骤(LexicalEnvironment),这与使用let时会有关。所以,如果你使用了let而不是var,let的变量除了作用域是在for区块中,而且会为每次循环执行建立新的词法环境(LexicalEnvironment),拷贝所有的变量名称与值到下个循环执行。以最简单的方式改写原先的问题中的代码,相当于下面这样写:
var a = [];
{ let k;
for (k = 0; k < 10; k++) {
let i = k; //注意这里,每次循环都会创建一个新的i变量
a[i] = function () {
console.log(i);
};
}
}
a[6](); // 6