尽管函数作用域是最常见的作用域单元,当然也是现行大多数 JavaScript 中最普遍的设计 方法,但其他类型的作用域单元也是存在的,并且通过使用其他类型的作用域单元甚至可 以实现维护起来更加优秀、简洁的代码。
try...catch 非常少有人会注意到 JavaScript 的 ES3 规范中规定 try/catch 的 catch 分句会创建一个块作用域, catch 的参数变量仅在 catch 内部有效。
try{
throw undefined;
}catch(a){
a = 2;
console.log(a); // 2
}
console.log(a); // ReferenceError
ES6的标准使我们能够简单的创建块作用域,其中一个变量定义方式是let关键词定义。
let定义的变量具有以下的特点:
let隐形的创建块作用域({...})
let声明的变量不能进行变量提升,因此只能先定义,后使用
{ let a = 1;
console.log(a); // 1
}
console.log(a); // ReferenceError
let一个典型的应用就是在for循环里
我们看下面两个例子:
// 每秒输出一个5
for( var i = 0; i < 5 ; i++ ) {
setTimeout(() => {
console.log( i );
}, i *1000)}
// 依次输出0,1,2,3,4,时间间隔位1秒
for( let i = 0; i < 5 ; i++ ) {
setTimeout(() => {
console.log( i );
}, i *1000)}
其原因就是let形成了5个块作用域,使每次输出的变量都从本次循环的块作用域中获取。
当然我们还可以有其他方式做到第二种效果,我们将在 闭包,是真的美中说道。
除了 let 以外,ES6 还引入了 const,同样可以用来创建块作用域变量,但其值是固定的 (常量)。之后任何试图修改值的操作都会引起错误。
var fn = true;if (foo) {
var a=2;
const b = 3; // 包含在 if 中的块作用域常量 a=3;//正常! b=4;//错误!
}console.log( a );
// 3console.log( b ); // ReferenceError!
作用域链
作用域链是由当前作用域与上层一系列父级作用域组成,作用域的头部永远是当前作用域,尾部永远是全局作用域。作用域链保证了当前上下文对其有权访问的变量的有序访问。
var a = 2; function bar() { function fn() { console.log(a); } }bar(); // 2
上面代码是由3层作用域气泡组成,fn气泡中试图打印变量a,引擎在fn气泡中未找到a变量,于是去其父作用域气泡bar中寻找...以此类推直到找到全局作用域气泡,发现有变量a,将其值打印出来。如若没找到,报ReferenceError错误。