06-变量屏蔽
7.5 变量屏蔽
在JavaScript开发中,有一个常常引发混淆的场景是,不同作用域中存在相同名字的变量或常量。而当作用域一个挨着一个的时候,这种场景会显得相对直观一些:
{
// block 1
const x = 'blue';
console.log(x); // 打印 "blue"
}
console.log(typeof x); // 打印"undefined"; x 不在作用域内
{
// block 2
const x = 3;
console.log(x); // 打印"3"
}
console.log(typeof x); // 打印 "undefined"; x 不在作用域内
这个例子非常容易理解:有两个相互独立的变量,在不同的作用域中都以x命名。那么,如果作用域嵌套在一起有会发生什么呢?
{
// outer block
let x = 'blue';
console.log(x); // 打印 "blue"
{
// inner block
let x = 3;
console.log(x); // 打印"3"
}
console.log(x); // 打印 "blue"
}
console.log(typeof x); // 打印 "undefined"; x 不在作用域内
这个例子演示了变量屏蔽。在内部块中, x 是一个独立于外部块的(同名)变量,这样实际上屏蔽(或隐藏)了外部块定义的 x 。
这里有一个关键的地方需要理解,当执行到内部块时,一个新的变量 x 被定义,两个变量都在作用域内:只是没有办法访问外部块的变量(因为具有相同的名字)。相对而言,在前一个例子中,一个变量 x 进入作用域,然后退出,接着第二个变量x做了相同的事情。
为了更好地理解这点,看下面例子:
{
// outer block
let x = { color: "blue" };
let y = x; // y 和 x 引用同一个对象
let z = 3;
{
// inner block
let x = 5; // 外部 x 被屏蔽
console.log(x); // 打印 5
console.log(y.color); // 打印 "blue"; y(以及x在外部作用域)
指向的对象仍然在
// 作用域内
y.color = "red";
console.log(z); // 打印 3; 因为z没有被屏蔽
}
console.log(x.color); // 打印"red"; 因为object在内部作用域被修改
console.log(y.color); // 打印"red"; 因为x和y指向同一个对象
console.log(z); // logs 3
}
变量屏蔽有时候也叫变量阴影(即一个相同名字的变量会将外部作用域的变量屏蔽起来)。作者不喜欢这个术语,因为阴影通常不会将事物完全隐藏,只是使之变得灰暗。而当一个变量被屏蔽时,这个被屏蔽的变量就无法再通过名字去访问了。
到目前为止,大家应该清楚了解了作用域的层次结构了:可以在原有的作用域中进入一个新的作用域。此时会建立一个作用域链来决定哪些变量在作用域中:当前作用域链中的所有变量都在作用域内,并且(只要它们没有被屏蔽)都是可以被访问的。
变量屏蔽有时候也叫变量阴影(即一个相同名字的变量会将外部作用域的变量屏蔽起来)。作者不喜欢这个术语,因为阴影通常不会将事物完全隐藏,只是使之变得灰暗。而当一个变量被屏蔽时,这个被屏蔽的变量就无法再通过名字去访问了。