面试题答案
一键面试1. 作用域链变化情况分析
- 进入
outer
函数执行上下文:- 创建
outer
函数的执行上下文,此时作用域链顶端为outer
函数的活动对象(AO),其中包含outerVar
变量,该变量初始化为'outer'
。同时,作用域链还包含全局执行上下文的变量对象(VO,在浏览器环境中是window
对象)。 - 遇到
inner
函数定义,此时inner
函数的作用域链被创建,它的作用域链顶端为inner
函数自身的活动对象(暂时为空,因为函数还未执行),然后指向outer
函数的活动对象,最后指向全局执行上下文的变量对象。
- 创建
- 进入
inner
函数执行上下文:- 创建
inner
函数的执行上下文,inner
函数的活动对象成为作用域链顶端,其中包含innerVar
变量,初始化为'inner'
。作用域链依次为inner
函数的活动对象、outer
函数的活动对象、全局执行上下文的变量对象。
- 创建
- 进入块级作用域:
- 当进入块级作用域
{}
时,由于let
具有块级作用域特性,会创建一个新的块级作用域,其作用域链顶端为该块级作用域的活动对象,包含blockVar
变量,初始化为'block'
。作用域链依次为块级作用域的活动对象、inner
函数的活动对象、outer
函数的活动对象、全局执行上下文的变量对象。所以在块级作用域内,console.log(outerVar, innerVar, blockVar);
能正确输出outer inner block
。
- 当进入块级作用域
- 离开块级作用域:
- 块级作用域结束,块级作用域的活动对象从作用域链中移除。此时
inner
函数作用域链顶端为inner
函数的活动对象。在console.log(outerVar, innerVar, blockVar);
处,由于blockVar
只存在于块级作用域内,此时块级作用域已结束,blockVar
不在作用域链中,会导致ReferenceError
。
- 块级作用域结束,块级作用域的活动对象从作用域链中移除。此时
2. 改为 var blockVar = 'block';
后的变化及原因
- 代码输出变化:如果将
let blockVar = 'block';
改为var blockVar = 'block';
,代码不会报错,且两次console.log
输出均为outer inner block
。 - 原因:
var
没有块级作用域,只有函数作用域。当var blockVar = 'block';
执行时,blockVar
会被提升到inner
函数的顶部(虽然只是声明被提升,初始化还是在原来位置)。所以在整个inner
函数内,blockVar
都在作用域链中(在inner
函数的活动对象里),因此两次console.log
都能访问到blockVar
并输出block
。