我们知道再ES5
中的var
和function
的申明都存在又变量提升,ES6
中的let
、const
则不存在有变量提升。
var变量提升
console.log(a);
var a =1;
// 等价于
var a ;
console.log(a); // undefined
a = 1;
js中的 function
也可以看作是变量,也存在变量提升
a(); // 1
function a() {
console.log(1);
}
总之
JavaScript引擎的工作方式是,先解析代码,获取所有被声明的变量,然后再一行一行地运行。这造成的结果,就是所有的变量的声明语句,都会被提升到代码的头部,这就叫做变量提升(hoisting)。
let深入理解
- let 声明的变量的作用域是块级的;
- let 不能重复声明已存在的变量;
- let 有暂时死区,不会被提升。
// case1
var liList = document.querySelectorAll('li') // 共5个li
for( var i=0; i<liList.length; i++){
liList[i].onclick = function(){
console.log(i)
}
}
// case2
var liList = document.querySelectorAll('li') // 共5个li
for( let i=0; i<liList.length; i++){
liList[i].onclick = function(){
console.log(i)
}
}
依次点击会出现5个 5 。如果把var 改为了let 就是分别打印0 , 1 , 2, 3, 4 了。
我的理解是:
- for(let i = 0; i< 5; i++) 这句话的圆括号之间存在有一个隐藏的作用域
- 再每次执行循环体的时候都会在循环体上下文中重新初始化一次
var liList = document.querySelectorAll('li') // 共5个li
for( let i=0; i<liList.length; i++){
let i = 隐藏作用域中的i // 看这里看这里看这里
liList[i].onclick = function(){
console.log(i)
}
}
// 那样的话,5 次循环,就会有 5 个不同的 i,console.log 出来的 i 当然也是不同的值。
下面我们再来仔细刨析一下具体声明的过程
var变量声明的创建、初始化和赋值过程
function fn(){
var a = 0;
var b = 1;
}
fn();
过程分析:
- 进入fn,为其创建一个环境
- 找出fn中所有声明的变量,在这个环境中创建这些变量(a,b)
- 将这些变量初始化为undefined
- 开始执行代码
- a 赋值 0,b 赋值 1;
这就解释了为什么在 var x = 1 之前 console.log(x) 会得到 undefined。
function的声明的创建、初始化、和赋值过程
f2(); // 2
function f2() {
console.log(2);
}
过程分析:
- 找到function声明的变量,在环境中创建
- 将这些变量初始化并赋值, function(){ console.log(2) }。
- 开始执行代码 fn(); //2
let声明的创建、初始化、赋值
{
let x = 1
x = 2
}
过程分析:
- 找到let变量在环境中创建,
- 开始执行代码 ,注意还没有进行初始化哦
- 执行x = 1; x 初始化为 1
- 执行x = 2; x 进行赋值
我们再来理解一下let 之前不能使用的原因
{
console.log(a);
let a = 1;
}
原因:
- console.log(a) 中的 a 指的是下面的 a,
- 执行 log 时 x 还没「初始化」,所以不能使用(也就是暂时性死区)
总结
let
的「创建」过程被提升了,但是初始化没有提升。var
的「创建」和「初始化」都被提升了。function
的「创建」「初始化」和「赋值」都被提升了。
最后看 const
,其实 const 和 let 只有一个区别,那就是 const 只有「创建」和「初始化」,没有「赋值」过程。
所谓暂时死区
,就是不能在初始化之前,使用变量。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。