前段时间阅读一本书《ESCMScript 6入门》,其中一章关于”let和const命令”有一段代码
var tmp = new Date()
function f(){
console.log(tmp)
if(false){
var tmp = 'hello world'
}
}
关于变量声明提升,思量许久,写了以下理解。
这段代码看似简单,其内部隐藏着重要的知识点“变量提升”这一现象。一般理解,调用f函数tmp要么打印出日期,要么打印出undefined,结果为后者。为何?
书本上仅仅这么说明:原因在于变量提升,导致内层tmp变量覆盖了外层tmp变量。
仅仅理解这句话并不能清楚的知道为何?
再来看下面的代码:
function f(){
var item1 = 1;
var item2 = 'str';
var item = [1]
}
经过编译器预处理类似变成:
function f(){
var item1,item2,item3;
item1 = 1;
item2 = 'str'
item3 = [1]
}
经过编译器处理只会将所有的变量进行统一的声明处理,所以,案例中经典的代码可以改为:
var tmp = new Date();
function f(){
var tmp;
console.log(tmp);
if(false){
var tmp = 'hello world';
}
}
总结:变量提升分为声明提升和定义提升,赋值或者初始话并不会被提升,而代码中出现if条件语句,虽然代码不会进入那个片段,但是编译器会预处理里面的代码,将声明tmp提升到f函数的最顶层,之所以覆盖全局,是因为内部已经声明了tmp变量,将外部全局变量对f函数的tmp的搜索值屏蔽,因函数存在声明,tmp变量就不再往上层去搜索值(原型链搜索),这与对象的原型方法搜索类似。
2018-09-06
上文是我在两个月前写的let变量提升的理解,但是自从面试之后,发现不仅仅那么简单,面试官这样问我:var存在变量声明提升,那let会么?按照MDN文档的解释,因为let存在暂时性死区,所以无法进行变量声明提升。但真的是这样简单的理解了么,下面我来把真正的原因讲一下。
在MDN文档描述了let的知识,总结有一下三条:
- let声明变量的作用是块级的;
- let不能重复声明一已经存在的变量;
- let存在暂时性死区,变量声明不会被提升。
虽然你理解了上面三个总结,但是你的理解和对let的全面理解并不深刻。
let是否存在变量提升问题?
其实所有声明变量的关键字,都存在变量声明提升这个功能,只不过有些可以表现出这个特性,如var,有些不能,如let,为什么let不能呢,暂时性死区又怎么作用于它呢?
MDN文档在let上前后进行了两次修改,所以其作者也是举起不定,我们翻看es文档,会发现一段话:
The environment of with statements cannot contain any lexical declaration so it doesn't need to be checked for var/let hoisting conflicts
。
这句话证明了let存在变量提升。提升不是一个技术名称,js变量被声明的过程:创建、初始化、赋值。
- var在创建和初始化的过程中都被提升;
- let在创建会被提升,而初始化没用被提升,因为暂时性死区(变量在未使用之前,不能使用该变量);
- 函数创建、初始化和赋值都被提升; const只有创建和初始化过程,没有赋值过程。
总结:
- let 具有变量提升过程,但不表现这个过程,因为存在暂时性死区;
- let 创建会被提升,但初始化不能被提升;
- 所有变量声明的关键字操作都存在变量提升这个过程,只是因为不同情况和性质导致是否需要表现出来。
(文章结合自己思考和参考一些资料,如有侵权请联系本人,会尽快处理)。