ES5 只有全局作用域和函数作用域,没有块级作用域。这会带来很多不合理的场景,比如说:
- 内层的变量可能会覆盖掉外层变量
var tmp = "123"; function f(){ console.log(tmp); if(false){ var tmp = "I m here"; } } f();//输出是undefined,原因在于变量提升,内部的tmp变量覆盖了外部的tmp
- 用来计数的循环变量泄漏为全局变量
var s = 'hello';
for(var i = 0;i <= s.length; i++){
console.log(s[i]);
}
上面的代码中,i是用来控制循环的,但是循环结束之后,它并没有消失,泄漏成了全局变量。
ES6的块作用域
块级作用域与函数说明
ES5中规定,函数只能在顶层作用域和函数作用域中使用,不能在块级作用域中使用
情况一
if(true){
function(){}
}
情况二
try{
function (){}
}catche(e){
}
这两种情况,在ES5中都是不合法的,但是在实际的浏览器运行中,为了兼容以前的代码,还是支持在块级作用域中声明函数的,因此这两种情况实际上都可以运行。不过,严格模式下还是会报错的。
//ES5 严格模式 报错
‘use strict’
if(true){
function f(){};
}
ES 6 引入了块级作用域,明确允许在块级作用域中声明函数
//ES6
if(true){
function f(){}; //不报错
}
ES6中规定,块级作用域之中,函数声明语句的行为类似于let,在块级作用域之外不可引用。
function f(){console.log("I am outside");}
(function(){
if(false){
//重复声明一次f
function f(){console.log("I am inside");}
}
f();
}())
上面的代码在ES5中运行,会得到“I am inside”,因为在if内声明的函数会被提到函数头部,实际运行代码如下:
function f(){console.log("I am outside")};
(function(){
function f(){console.log("I am inside")};
if(false){};
f();
}())
ES6的运行结果完全不一样,会得到“I am outside”.这是因为块级作用域里声明的函数类似于let,对作用域之外没有任何影响,实际运行代码如下:
function f(){console.log("I am outside")};
(function(){
f();
}())
但是,实际上在ES6浏览器上运行这段函数的时候,是会报错的。这是为什么呢?
原来,ES6改变了块级作用域中声明函数的处理规则,显然对老代码会产生影响,所以为了减轻因此而产生的不兼容问题,ES6在附录B中规定,浏览器的实现可以不遵守上面的规定,有自己的方式。
- 允许在块级作用域中声明函数
- 函数声明类似于var,即会提到全局作用域或者函数作用域的头部
- 同时,函数声明还会提升到所在的块级作用域的头部
根据这三条规则,在浏览器的ES6环境中,块级作用域内声明的函数,行为类似于var声明的变量。
//ES6 浏览器
function f(){console.log("I am outside")};
(function(){
var f = undefined;
if(false){function f(){console.log("I am inside")}};
f();
}())
//报错,uncaught TypeError:f is not a function
考虑到环境差异大,应该避免在块级作用域内声明函数。如果确实需要,也要写成函数表达式,而不是函数声明语句。
//函数声明语句
{
let a = "secret";
function f(){
return a;
}
}
//函数表达式
{
let a = 'secret';
let f = function(){return a};
}
//ES6 块级作用域允许声明函数的规则,只在使用大括号的情况下成立,如果没有使用大括号,就会报错。
//不报错
'use strict'
if(true){
function f(){};
}
//报错
//'use strict'
if(true)
function f(){};