# 作用域

# 什么是作用域?

作用域是根据名称查找变量一套规则

作用域有两种工作模型:词法作用域动态作用域JS没有动态作用域)。

作用域是怎么工作的?

  1. 收集维护代码中声明的变量
  2. 使用一套规则,确定当前执行的代码对这些变量访问权限

# 什么是作用域嵌套?

一个块或函数放在另一个块或函数中时,就是作用域嵌套。

作用域嵌套时,怎么查找变量?

  • 在自己当前作用域中查找
    • 找到:结束
    • 没找到:到外层作用域查找,直到全局作用域(最外层)为止

# 什么是 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)