在讲解es6以前,咱们必需要提一下es5中的var,也就是曾经的那个错误。es6
if (condition) {
var value = 1;
}
console.log(value);
复制代码
很简单的分析一下,初学者可能会认为,只有在condition为true的时候,value才会被赋值,若是condition为false的时候,代码应该会报错才对!可是,事实并非这样的,浏览器在执行这段代码的时候,并非这么解析的!浏览器
var value;
if (condition) {
value = 1;
}
console.log(value);
复制代码
这样,很容易咱们就能判断出,若是condition的值为false的时候,console出来的值为undefined。bash
缘由就是咱们常说的,变量提高。闭包
为了增强对变量生命周期的控制,ECMAScript 6 引入了块级做用域。函数
块级做用域存在于:post
函数内部ui
块中(字符 { 和 } 之间的区域)es5
let和const的特色:spa
1.不会被提高(真的是这样的吗?)3d
if (condition) {
let value = 1;
}
console.log(value); // Uncaught ReferenceError: value is not defined
复制代码
在代码块外面访问,直接断定,未定义。
2.重复声明报错
var value = 1;
let value = 2; // Uncaught SyntaxError: Identifier 'value' has already been declared
复制代码
这在之前是彻底能够的,后定义的回覆盖之前的。
3.不绑定全局做用域
let value = 1 ;
console.log(window.value);
复制代码
const也是相同的,都访问不到。
const和let的区别:
const 用于声明常量,其值一旦被设定不能再被修改,不然会报错。
值得一提的是:const 声明不容许修改绑定,但容许修改值。这意味着当用 const 声明对象时:
const data = {
value: 1
}
// 没有问题
data.value = 2;
data.num = 3;
// 报错
data = {}; // Uncaught TypeError: Assignment to constant variable.
复制代码
临时死区(Temporal Dead Zone),简写为 TDZ。
let 和 const 声明的变量不会被提高到做用域顶部,若是在声明以前访问这些变量,会致使报错。
console.log(typeof value); // Uncaught ReferenceError: value is not defined
let value = 1;
复制代码
来个例子~
var value = "global";
// 例子1
(function() {
console.log(value);
let value = 'local';
}());
// 例子2
{
console.log(value);
const value = 'local';
};
复制代码
结果是:都报错了~!
这是由于 JavaScript 引擎在扫描代码发现变量声明时,要么将它们提高到做用域顶部(遇到 var 声明),要么将声明放在 TDZ 中(遇到 let 和 const 声明)。访问 TDZ 中的变量会触发运行时错误。只有执行过变量声明语句后,变量才会从 TDZ 中移出,而后方可访问。 循环中的块级做用域:
var funcs = [];
for (var i = 0; i < 3; i++) {
funcs[i] = function () {
console.log(i);
};
}
funcs[0](); // 3
复制代码
如何改变现状呢?我要的是funcs0 == 0
在没有es6 以前,这个事儿麻烦了,还得使用闭包的方式!
var funcs = [];
for (var i = 0; i < 3; i++) {
funcs[i] = (function(i){
return function() {
console.log(i);
}
}(i))
}
funcs[0](); // 0
复制代码
ES6 的 let 为这个问题提供了新的解决方法:
var funcs = [];
for (let i = 0; i < 3; i++) {
funcs[i] = function () {
console.log(i);
};
}
funcs[0](); // 0
复制代码
问题在于,上面讲了 let 不提高,不能重复声明,不能绑定全局做用域等等特性,但是为何在这里就能正确打印出 i 值呢? 若是是不重复声明,在循环第二次的时候,又用 let声明了i,应该报错呀,就算由于某种缘由,重复声明不报错,一遍一遍迭代,i 的值最终仍是应该是 3 呀,还有人说 for 循环的 设置循环变量的那部分是一个单独的做用域,就好比:
for (let i = 0; i < 3; i++) {
let i = 'abc';
console.log(i);
}
// abc
// abc
// abc
复制代码
这个例子是对的,若是咱们把 let 改为 var 呢?
for (var i = 0; i < 3; i++) {
var i = 'abc';
console.log(i);
}
// abc
复制代码
经查, for 循环中使用 let 和 var,底层会使用不一样的处理方式。 简单的来讲,就是在 for (let i = 0; i < 3; i++) 中,即圆括号以内创建一个隐藏的做用域,这就能够解释为何:
for (let i = 0; i < 3; i++) {
let i = 'abc';
console.log(i);
}
// abc
// abc
// abc
复制代码
而后每次迭代循环时都建立一个新变量,并以以前迭代中同名变量的值将其初始化。
var funcs = [];
for (let i = 0; i < 3; i++) {
funcs[i] = function () {
console.log(i);
};
}
funcs[0](); // 0
复制代码
至关于:
// 伪代码
(let i = 0) {
funcs[0] = function() {
console.log(i)
};
}
(let i = 1) {
funcs[1] = function() {
console.log(i)
};
}
(let i = 2) {
funcs[2] = function() {
console.log(i)
};
};
复制代码
首先明确一点:提高不是一个技术名词。
要搞清楚提高的本质,须要理解 JS 变量的 这就解释了为何在 let x 以前使用 x 会报错:
假设有以下代码:
function fn(){
var x = 1
var y = 2
}
fn()
复制代码
在执行 fn 时,会有如下过程(不彻底):
进入 fn,为 fn 建立一个环境。
找到 fn 中全部用 var 声明的变量,在这个环境中「建立」这些变量(即 x 和 y)。
将这些变量「初始化」为 undefined。
开始执行代码
x = 1 将 x 变量「赋值」为 1
y = 2 将 y 变量「赋值」为 2
也就是说 var 声明会在代码执行以前就将「建立变量,并将其初始化为 undefined」。
这就解释了为何在 var x = 1 以前 console.log(x) 会获得 undefined。
接下来来看 function 声明的「建立、初始化和赋值」过程
假设代码以下:
fn2()
function fn2(){
console.log(2)
}
复制代码
JS 引擎会有一下过程:
找到全部用 function 声明的变量,在环境中「建立」这些变量。
将这些变量「初始化」并「赋值」为 function(){ console.log(2) }。
开始执行代码 fn2()
也就是说 function 声明会在代码执行以前就「建立、初始化并赋值」。
接下来看 let 声明的「建立、初始化和赋值」过程
{
let x = 1
x = 2
}
复制代码
咱们只看 {} 里面的过程:
找到全部用 let 声明的变量,在环境中「建立」这些变量
开始执行代码(注意如今尚未初始化)
执行 x = 1,将 x 「初始化」为 1(这并非一次赋值,若是代码是 let x,就将 x 初始化为 undefined)
执行 x = 2,对 x 进行「赋值」
let x = 'global'
{
console.log(x) // Uncaught ReferenceError: x is not defined
let x = 1
}
复制代码
这就解释了为何在 let x 以前使用 x 会报错: 缘由有两个:
console.log(x) 中的 x 指的是下面的 x,而不是全局的 x
执行 log 时 x 还没「初始化」,因此不能使用(也就是所谓的暂时死区)
看到这里,你应该明白了 let 到底有没有提高:
let 的「建立」过程被提高了,可是初始化没有提高。
var 的「建立」和「初始化」都被提高了。
function 的「建立」「初始化」和「赋值」都被提高了。
最后看 const,其实 const 和 let 只有一个区别,那就是 const 只有「建立」和「初始化」,没有「赋值」过程!!!
这四种声明,用下图就能够快速理解:
所谓暂时死区,就是不能在初始化以前,使用变量。