【02】块级作用域
魔芋总结:
为什么要有块级作用域?
- 01,内层变量覆盖外层变量,因为变量提升。函数提升。
- 02,用来计数的循环变量泄露为全局变量。循环结束后,它并没有消失,泄露成了全局变量。
03,ES6允许块级作用域的任意嵌套。
04,内层作用域可以和外层作用域定义同名的变量。
05,立即执行匿名函数(IIFE)不再必要了。
06,函数的作用域,在其定义的块级作用域之内。
需要注意的是,如果在严格模式下,函数只能在顶层作用域和函数内声明,其他情况(比如if代码块、循环代码块)的声明都会报错。
块级作用域
为什么需要块级作用域?
ES5只有全局作用域和函数作用域,没有块级作用域,这带来很多不合理的场景。
第一种场景,内层变量可能会覆盖外层变量。
var tmp = new Date();
function f(){
console.log(tmp);
if (false){
var tmp = "hello world";
}
}
f() // undefined
上面代码中,函数f执行后,输出结果为
undefined
,原因在于变量提升,导致内层的tmp变量覆盖了外层的tmp变量。第二种场景,用来计数的循环变量泄露为全局变量。
var s = 'hello';
for (var i = 0; i < s.length; i++){
console.log(s[i]);
}
console.log(i); // 5
上面代码中,变量i只用来控制循环,但是循环结束后,它并没有消失,泄露成了全局变量。
ES6的块级作用域
let
实际上为JavaScript新增了块级作用域。function f1() {
let n = 5;
if (true) {
let n = 10;
}
console.log(n); // 5
}
上面的函数有两个代码块,都声明了变量
n
,运行后输出5。这表示外层代码块不受内层代码块的影响。如果使用var
定义变量n
,最后输出的值就是10。ES6允许块级作用域的任意嵌套。
{{{{{let insane = 'Hello World'}}}}};
上面代码使用了一个五层的块级作用域。外层作用域无法读取内层作用域的变量。
{{{{{let insane = 'Hello World'}
console.log(insane); // 报错
}}}};
内层作用域可以定义外层作用域的同名变量。
{{{{let insane = 'Hello World';{let insane = 'Hello World';}}}}};
块级作用域的出现,实际上使得获得广泛应用的立即执行匿名函数(IIFE)不再必要了。
// IIFE写法
(function () {var tmp = ...;...}());
// 块级作用域写法
{let tmp = ...;...}
另外,ES6也规定,函数本身的作用域,在其所在的块级作用域之内。
function f() {
console.log('I am outside!');
}
(function () {
if (false) { // 重复声明一次函数f
function f() {
console.log('I am inside!');
}
}
f();
}());
上面代码在ES5中运行,会得到“I am inside!”,但是在ES6中运行,会得到“I am outside!”。这是因为ES5存在函数提升,不管会不会进入
if
代码块,函数声明都会提升到当前作用域的顶部,得到执行;而ES6支持块级作用域,不管会不会进入if代码块,其内部声明的函数皆不会影响到作用域的外部。{
let a = 'secret';
function f() {
return a;
}
}
f() // 报错
上面代码中,块级作用域外部,无法调用块级作用域内部定义的函数。如果确实需要调用,就要像下面这样处理。
let f;
{let a = 'secret';
f = function () {
return a;
}}
f(); // "secret"
**