首先,在ES5的标准中,函数是不能在块级作用域中声明的。
if(true){
function foo(){}
}
try{
function foo(){}
} catch(e){
}
也就是说类似以上两种情况在ES5中都是非法的。然而,浏览器为了兼容旧代码所以并没有遵守这一规定。所以上面的代码在浏览器中运行并不会出错。
而在ES6的标准中引入了块级作用域,也确定了函数是能够被声明在块级作用域内的。同时还规定了,在块级作用域中声明的函数类似于使用let
声明的变量,只能在块级作用域内有效。
function foo(){console.log('Hello');}
(function (){
if(false){
function foo(){console.log('World');}
}
foo();
}());
上述代码若在ES5中运行,则会输出"World",原因则是因为发生了函数提升。即真正执行的代码如下,
function foo(){console.log('Hello');}
(function (){
function foo(){console.log('World');}
if(false){
}
foo();
}());
function foo(){console.log('Hello');}
(function (){
if(false){
function foo(){console.log('World');}
}
foo();
}());
但若是按ES6的标准,上面的代码应该输出的是"Hello",这样也是符合直觉的一个结果。因为按照前文的说法,声明的在if
语句中的函数应该无法影响外部的作用域。
但是运行结果却让人大跌眼镜,
那么问题出在哪儿呢?原因还是出在对老代码的兼容性上,所以实现跟标准还是存在着出入。
- 允许在块级作用域内声明函数。
- 函数声明类似于
var
,即会提升到全局作用域或函数作用域的头部。 - 同时,函数声明还会提升到所在的块级作用域的头部。
那么按照这三条规则来看,上面的代码应该是下面的样子的,
function foo(){console.log('Hello');}
(function (){
var foo = undefined;
if(false){
function foo(){console.log('World');}
}
foo();
}());
所以,这样就能理解为啥报错会提示foo
不是一个函数了,因为它是一个undefined
。