作用域及作用域链
在前面的示例中,所有内容都是全局作用域的,这意味着我们可以从代码中的任何位置访问它 。现在,介绍下私有作用域以及如何定义作用域 。
函数/词法作用域
考虑如下代码:
function a() { var myOtherVar = 'inside A' b()}function b() { var myVar = 'inside B' console.log('myOtherVar:', myOtherVar) function c() { console.log('myVar:', myVar) } c()}var myOtherVar = 'global otherVar'var myVar = 'global myVar'a()需要注意以下几点:
- 全局作用域和函数内部都声明了变量
- 函数 c现在在函数 b中声明
myOtherVar: "global otherVar"myVar: "inside B"执行步骤:
- 全局创建和声明 - 创建内存中的所有函数和变量以及全局对象和 this
- 执行 - 它逐行读取代码,给变量赋值,并执行函数a
- 函数a创建一个新的上下文并被放入堆栈,在上下文中创建变量 myOtherVar,然后调用函数b
- 函数b 也会创建一个新的上下文,同样也被放入堆栈中
上面提到每个新上下文会创建的外部引用,外部引用取决于函数在代码中声明的位置 。
- 函数b试图打印 myOtherVar,但这个变量并不存在于函数b中,函数b 就会使用它的外部引用上作用域链向上找 。由于函数b是全局声明的,而不是在函数a内部声明的,所以它使用全局变量myOtherVar 。
- 函数c执行步骤一样 。由于函数c本身没有变量 myVar,所以它它通过作用域链向上找,也就是函数b,因为 myVar是函数b内部声明过 。

文章插图
请记住,外部引用是单向的,它不是双向关系 。例如,函数b不能直接跳到函数c的上下文中并从那里获取变量 。
最好将它看作一个只能在一个方向上运行的链(范围链) 。
- a -> global
- c -> b -> global
块作用域
下面代码中,我们有两个变量和两个循环,在循环重新声明相同的变量,会打印什么(反正我是做错了)?
function loopScope () { var i = 50 var j = 99 for (var i = 0; i < 10; i++) {} console.log('i =', i) for (let j = 0; j < 10; j++) {} console.log('j =', j)}loopScope()打印结果:
【JavaScript引擎运行原理】i = 10j = 99第一个循环覆盖了 vari,对于不知情的开发人员来说,这可能会导致bug 。
第二个循环,每次迭代创建了自己作用域和变量 。这是因为它使用 let关键字,它与 var相同,只是 let有自己的块作用域 。另一个关键字是 const,它与 let相同,但 const常量且无法更改(指内存地址) 。
块作用域由大括号 {} 创建的作用域
再看一个例子:
function blockScope () { let a = 5 { const blockedVar = 'blocked' var b = 11 a = 9000 } console.log('a =', a) console.log('b =', b) console.log('blockedVar =', blockedVar)}blockScope()打印结果:
a = 9000b = 11ReferenceError: blockedVar is not defined
- a是块作用域,但它在函数中,而不是嵌套的,本例中使用 var是一样的 。
- 对于块作用域的变量,它的行为类似于函数,注意 varb可以在外部访问,但是 constblockedVar不能 。
- 在块内部,从作用域链向上找到 a 并将 leta更改为 9000 。
事件循环(Event Loop)
接下来看看事件循环 。这是回调,事件和浏览器API工作的地方

文章插图
我们没有过多讨论的事情是堆,也叫全局内存 。它是变量存储的地方 。由于了解JS引擎是如何实现其数据存储的实际用途并不多,所以我们不在这里讨论它 。
来个异步代码:
function logMessage2 () { console.log('Message 2')}console.log('Message 1')setTimeout(logMessage2, 1000)console.log('Message 3')上述代码主要是将一些 message 打印到控制台 。利用 setTimeout函数来延迟一条消息 。我们知道js是同步,来看看输出结果
Message 1Message 3Message 2
