JavaScript 系列之作用域(一)

这是我参与8月更文挑战的第5天,活动详情查看:8月更文挑战

一、执行上下文 (EC)

当 JavaScript 代码执行一段可执行代码(executable code)时,会创建对应的执行上下文(execution context),可以理解是一个对象

当执行 JS 代码时,会产生三种执行上下文:

  • 全局执行上下文
  • 函数执行上下文
  • eval 执行上下文

每个执行上下文中都有三个重要的属性:

  • 变量对象(Variable object,VO),当进入执行上下文时,这时候还没有执行代码,变量对象包含变量函数声明函数的形参,该属性只能在全局上下文中访问
  • 作用域链(Scope chain),JS 采用词法作用域,也就是说变量的作用域是在定义时就决定了
  • this

二、执行上下文栈 (ECS)

为了管理执行上下文,所以 JavaScript 引擎创建了执行上下文栈(Execution context stack,ECS)。

2.1 例一

var a = 10;
function foo(i) {
  var b = 20;
}
foo();
复制代码

对于上述代码,执行栈中有两个上下文:全局上下文函数 foo 上下文

试想当 JavaScript 开始要解释执行代码的时候,最先遇到的就是全局代码,所以初始化的时候首先就会向执行上下文栈压入一个全局执行上下文,我们用 globalContext 表示它,并且只有当整个应用程序结束的时候,stack 才会被清空。

stack = [ globalContext, fooContext]
复制代码

对于全局上下文来说,VO 大概是这样的:

globalContext.VO === globe
globalContext.VO = {
  a: undefined,
  foo: <Function>,
}
复制代码

对于函数 foo 来说,VO 不能访问,只能访问到活动对象(AO)

// arguments 是函数独有的对象(箭头函数没有)
// 该对象是一个伪数组,有 `length` 属性且可以通过下标访问元素
// 该对象中的 `callee` 属性代表函数本身
// `caller` 属性代表函数的调用者
fooContext.VO === foo.AO
fooContext.AO = {
  i: undefined,
  b: undefined,
  arguments: <>
}

复制代码

对于作用域链,可以把它理解成包含自身变量对象和上级变量对象的列表,通过 [[Scope]] 属性查找上级变量

fooContext.[[Scope]] = [
  globalContext.VO
]

fooContext.Scope = fooContext.[[Scope]] + fooContext.VO

// so
fooContext.Scope = [
  fooContext.VO,
  globalContext.VO
]
复制代码

2.2 例二

function fun3() {
  console.log("fun3");
}
function fun2() {
  fun3();
}
function fun1() {
  fun2();
}
fun1();
复制代码

当执行一个函数的时候,就会创建一个执行上下文,并且压入执行上下文栈,当函数执行完毕的时候,就会将函数的执行上下文从栈中弹出。知道了这样的工作原理,让我们来看看如何处理上面这段代码:

// 伪代码

// fun1()
ECStack.push(<fun1> functionContext);
// ECStack = [globalContext, fun1Context]

// fun1中竟然调用了fun2,还要创建fun2的执行上下文
ECStack.push(<fun2> functionContext);
// ECStack = [globalContext, fun1Context, fun2Context]

// 擦,fun2还调用了fun3!
ECStack.push(<fun3> functionContext);
// ECStack = [globalContext, fun1Context, fun2Context, fun3Context]

// fun3执行完毕
ECStack.pop();
// ECStack = [globalContext, fun1Context, fun2Context]

// fun2执行完毕
ECStack.pop();
// ECStack = [globalContext, fun1Context]

// fun1执行完毕
ECStack.pop();
// ECStack = [globalContext]

// javascript接着执行下面的代码,但是ECStack底层永远有个globalContext
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享