# 词法作用域
作用域共有两种主要的工作模式:词法作用域,动态作用域
# 词法阶段
词法作用域是由你在写代码时将变量和块作用域写在哪里来决定的。因此当词法分析器处理代码时会保持作用域不变(大部分情况下是这样的)
function foo (a) {
var b = a * 2
function bar (c) {
console.log(a, b, c)
}
bar(b * 3)
}
foo(2) // 2 4 12
2
3
4
5
6
7
8
9
10
11
可以将他们想象成几个逐级包含的气泡

气泡1包含着整个全局作用域,其中只有一个标识符:foo。气泡2包含着foo所创建的作用域,有三个标识符:a,bar和b。气泡3包含这bar所创建的作用域,其中只有一个标识符:c。作用域气泡由其对应的作用域块代码写在哪里决定,他们是逐级包含的
引擎在执行console.log(...) 声明,查找 a、b、c三个变量的引用。首先从内部作用域(bar(...)函数的作用域)开始查找。此时无法找到 a, 因此会去上一级到foo(...)的作用域中继续查找。此时能找到a。同理可以查找到 b 、c。
作用域查找会在找到第一个匹配的标识符时停止。在多层嵌套的作用域中可以定义同名的标识符,这叫做遮蔽效应(内部的标识符,遮蔽了外部的标识符)。抛开遮蔽效应,作用域查找始终从运行时所处的最内部作用域开始,逐级向外或者说向上进行,直到遇见第一个匹配标识符为止。
无论函数在哪里被调用,也无论他如何被调用,他的词法作用域都只由函数被声明时所处的位置决定。
# 欺骗词法
# eval
eval(...) 函数可以接受一个字符串为参数,并将其中的内容视为好像在书写时就存在于程序中这个位置的代码。换句话说,可以在你写的代码中用程序生成代码病运行,就好像代码是在那个位置一样
在执行eval 之后的代码时,引擎并不不知道或在意前面的代码是以动态形式插入进来,并对词法作用域的环境进行修改的。引擎只会如往常的进行词法作用域查找。
function foo(str, a) {
eval(str) // 欺骗
console.log(a, b) // 1 3
}
var b = 2
foo('var b = 3', 1)
2
3
4
5
6
← 作用域 函数作用域和块作用域 →