先看题目
function bs(){
console.log(1);
}
function fn(){
bs();
if(false){
function bs(){
console.log(2)
}
}
}
fn();
表面上,这是一道js函数变量提升的问题,函数内输出2。真的是这样吗?
不是的,本题在当前浏览器下输出为bs is not a function错误
(点击直接跳转到结论)
原因分析
这道题直觉上反常识,似乎fn中bs函数的作用域没有提升,因此这里的作用域一定出了问题,首先猜测是浏览器问题,因此分别测试了ES5和ES6环境
ES5环境
如果在ES5环境(IE浏览器选择低版本ES5环境)下,本题输出2,函数定义会提升到fn函数作用域顶部:
function bs(){
console.log(1);
}
function fn(){
function bs(){
console.log(2)
}
bs(); // 2
if(false){}
}
fn();
ES6环境
根据阮一峰ES6教程“ES6规定,块级作用域之中,函数声明语句的行为类似于let,在块级作用域之外不可引用”,即其执行效果是这样的:
function bs(){
console.log(1);
}
function fn(){
var bs = undefined;
bs();
if(false){
function bs() { console.log(2) }
}
}
fn();
其行为类似于ES6块级作用域中let定义前的暂时性死区
(function a() {
console.log(b) // b is not defined
let b;
})();
重大误区
错误理解ES6作用域
因为本题的执行结果如同函数bs的声明没有提升,容易误以为是if条件的花括号限制导致,进而认为ES6块级作用域是用花括号创建的,从而认为if条件的花括号构成块级作用域,并用以下代码测试得到“正确”结果
function bs(){
console.log(1);
}
function fn(){
bs();
{
function bs(){
console.log(2)
}
}
}
fn();
实际上,这种歪打正着是非常要命的,ES6并未提到有花括号直接构成块级作用域的说法,ES6的块级作用域只能是花括号+let/const定义共同构建的,两者缺一不可
结论
- 本题是ES6环境下函数作用域中声明函数的坑
- ES5浏览器环境下输出2,是浏览器自行实现的结果。
- 根据规范,ES5的函数中不应使用函数声明,但是根据浏览器的实现:在函数中使用函数声明则声明会提升
- ES6的函数中允许使用函数声明,但其行为类似于let定义,有暂时性死区的效果
- 无论是ES5还是ES6,都不应该在函数作用域中写函数声明,写成函数表达式更容易理解
- 这道题反映了浏览器实现、JS语言规范的问题,提醒自己要重视规范,遇到问题要刨根问底从规范着手解决问题,只有阅读规范才能更深入理解语言细节。
评论 0
还没有评论,来抢个沙发吧。