# 作用域
# 什么是作用域?
作用域
是根据名称
查找变量
的一套规则。
作用域
有两种工作模型:词法作用域
和动态作用域
(JS
没有动态作用域
)。
作用域是怎么工作的?
收集
并维护
代码中声明的变量
- 使用
一套规则
,确定当前执行的代码
对这些变量
的访问权限
# 什么是作用域嵌套?
一个
块或函数
放在另一个块或函数
中时,就是作用域嵌套。
作用域嵌套时,怎么查找变量?
- 在自己
当前作用域
中查找- 找到:结束
- 没找到:到
外层作用域
查找,直到全局作用域
(最外层)为止- 找到: 结束
- 没找到:会有两种情况,
创建变量
或报错
。具体查看LHS
和RHS
# 什么是 LHS 和 RHS ?
# LHS
可简单理解为
等号左边
变量的取值操作。
目的是:找到变量的容器(指针)
属于:赋值
操作
非严格模式
,找不到该变量会在全局作用域
创建一个。
严格模式
,找不到会报错。
# RHS
可简单理解为
等号右边
变量的取值操作。
目的是:找到变量的值(源值)
属于:取值
操作
找不到抛ReferenceError
异常。
ReferenceError 与作用域判别失败相关。
TypeError 表示作用域中存在,但对结果的操作是非法的。
# 什么是遮蔽效应?
作用域
会从内往外
查找变量,找到第一个匹配的变量
时停止,所以是内部遮蔽外部。
多层嵌套
的作用域
中定义同名变量
,就会产生遮蔽效应
全局变量被遮蔽还能访问吗?
全局变量
会自动成为全局对象的属性
。- 因此可以通过
window.xx
(假设是浏览器环境)来访问被遮蔽的全局变量
。 - 非全局的变量被遮蔽了,就无法被访问了
# 如何在运行时修改词法作用域?
eval()
和with
会在运行时
修改(欺骗)词法作用域
。
# eval()
eval()
接受一个字符串
作为参数,并将字符串
在运行时
当作可执行代码
,并执行
eval('var a = 1;'); // 运行在全局,会在全局对象上声明变量a
eval('console.log(a)'); // 打印2
非严格模式
,eval()
会修改
所处的词法作用域
。
严格模式
,eval()
有自己的词法作用域,无法修改所处的作用域。
# with
重复引用修改同一个对象的多个属性的快捷方式。(
严格模式
下不能使用with
)
// 修改obj值的两种方式
var obj = { num1: 0, num2: 1 };
// 方法一:重复调用
obj.num1 = 1;
obj.num2 = 2;
// 方法二:快捷方式
with (obj) {
num1 = 1;
num2 = 2;
num3 = 3; // 因为会进行LHS查询,num3会被泄漏到全局作用域!!!
}
- 若with引用的属性在原对象中
存在
,则直接赋值。 - 若with引用的属性在原对象中
不存在
,进行LHS查询,来进行赋值。
# 性能问题
eval()
和with
会在运行时
修改或创建新的作用域
。引擎会在编译阶段
会对代码进行静态分析
(是的js也有编译阶段,在运行时进行),确定变量
和函数
的位置,方便在执行过程中
快速找到变量和函数。如果在代码中发现eval()
或with
,将导致静态分析不准,此时就会放弃进行静态分析
。若代码大量存在eval()
和with
,将导致代码运行变慢。
如果觉得内容对你有帮助,请点个赞和关注,你们的鼓励是我持续更新下去的动力,比心。
如需转载请注明出处,感恩。
更多内容可先关注GitHub (opens new window)
有几种作用域? →