弟弟的ES6笔记——块级作用域和do表达式
块级作用域
由来
大家知道ES6之前的标准是没有块级作用域这个概念的,只有全局作用域和函数作用域,这就容易导致一些我们不希望看到的现象发生。
内层变量可能覆盖外层变量
var value = new Data()
function f(){
console.log(value);
if(false){
var value = 'hello world';
}
}
f();// undefined
变量提升与函数提升不同在于变量提升是可以提升到所在作用域的开始部分,而函数提升只能在全局环境下声明才能提升到开头。
function f(){
console.log(value); //undefined
fn(); //Reference
if(false){
var value = '123';
function fn(){
console.log("3223");
}
}
}
f();
console.log(value); //Reference
fn(); //Reference
ES6的块级作用域
let的出现,实际上为JavaScript提供了块级作用域
function f1(){
let value = 5;
if(true){
let value = 3;
}
console.log(value); // 5
}
很明显上面代码中的外层value不会受到内层value的影响,说明可以声明相同的变量名,但如果使用var声明value那么就将会输出3了。
{{
{
let value = 2333;
}
console.log(value); //Reference
}}
ES6允许多层块级作用域的任意嵌套,并且外层作用域无法读取内层的声明的变量。
块级作用域和函数声明
ES5规定,函数只能在顶层作用域和函数作用域之中声明,不能在块级作用域声明 。
//情况一
if(true){
function f(){}
}
//情况二
try{
function f(){}
}catch(e){
//...
}
以上两种情况在ES5中是非法的。
但是,在浏览器没有遵守这个规定,为了兼容以前的旧代码,还是支持在块级作用域之中声明函数,因此以上两种情况都可以运行,并不会报错。
ES6引入了块级作用域,明确允许在块级作用域之中声明函数。ES6规定,在块级作用之中,函数声明语句的行为类似于let,在块级作用域之外不可引用。
function f(){
console.log('out2333');
}
(function () {
if(false) {
// 重复声明一次函数f
function f() {
console.log('in2333');
}
}
f();
});
以上代码在ES5中运行会得到“I am inside”,因为在if内声明的函数f会被提升到函数头部,实际运行的代码如下。
//ES5环境
function f() {
console.log('out2333');
}
(function (){
function f() {
console.log('in2333')
}
if(false){}
f();
});
而在ES6中运行就完全不一样了,理论上会得到’out2333’。因为块级作用域内声明的函数类似于let,对作用之外没有影响。但是,如果真的在ES6浏览器中运行上面的代码,是会报错的,这是为什么呢?
原来,如果改变了块级作用内声明的函数的处理机制,显然会对旧代码产生影响。为了减轻因此产生的不兼容性问题ES6规定浏览器实现可以不遵守部分规定而有自己的行为方式,具体如下:
- 允许在块级作用域内声明函数
- 函数声明类似于var,即会提升到全局作用域或函数作用域头部
- 同时,函数声明还会提升到所在的块级作用域的头部
上面3条规则只对ES6的浏览器实现有效,其他环境的实现不用遵守,仍旧将块级作用域的函数声明当作let处理即可。
既然环境导致的行为差异如此之大,那么我们应该避免在块级作用之内声明函数,如果确实需要,也应该写成函数表达式的形式,而不是函数声明语句
{
// 函数声明
let value = 2333;
function f(){
return value;
}
// 函数表达式
let value = 2333;
let f = function(){
return value;
};
}
还有一个大家需要注意的地方,ES6的块级作用域允许声明函数的规则只在使用大括号的情况下成立,反之则会报错
// 不报错
if(true){
function f(){}
}
// 报错
if(true)
function f(){}
do 表达式
本质上,块级作用域是一个语句,将多个操作封装在一起,没有返回值
现在有一个提案(未实现),使得块级作用域可以变为表达式,即可以返回值
{
let value = do {
let f = functon(){};
f = f + 1;
};
}
上面的value会得到整个块级作用域的返回值
因为此提案为实现,所以上面的运行结果小编也不能肯定
最后祝大家复习考试周复习顺利,逢考必过。