hzzzzzzzq / Blog

这是我记录博客的地方,希望能多写一些技术博客,JS、ES、React、Vue等
14 stars 0 forks source link

深入理解JavaScript系列(4):作用域链 #45

Open hzzzzzzzq opened 2 years ago

hzzzzzzzq commented 2 years ago

JavaScript 深入之作用域链

在[《JavaScript 深入之执行上下文栈》]()中提到,当 JavaScript 代码执行代码时,就会创建一个执行上下文。

对于执行上下文,都有三个重要属性:

我们来介绍一下作用域链。

作用域链

在查找变量的时候,会先从当前上下文的变量对象中查找,如果没找到,就会从上一级的执行上下文的变量对象中查找,直到找到全局对象为止。

而这样由多个执行上下文的变量对象构成的链表就时作用域链。

函数创建

每一个函数都有自己的作用域,内部有一个 [[scope]] 属性,当函数创建时,就会保存所有父级变量对象到其中,简单来说,[[scope]] 就是所有父级变量对象的层级链。 注意:[[scope]] 并不能代表完整的作用域链。

我们来看一个例子吧

function fn1() {
  function fn2() {
    function fn3() {}
  }
}

函数创建时,我们来看看每一个函数的 [[scope]] 为:

fn1.[[scope]] = [
  globalContext.VO
];
fn2.[[scope]] = [
  fn1Context.AO,
  globalContext.VO
];
fn3.[[scope]] = [
  fn2Context.AO,
  fn1Context.AO,
  globalContext.VO
]

函数激活

当函数激活时,进入函数上下文,创建 VO/AO 后,就会将活动对象添加到作用域链的前端。

在执行上下文中,我们将作用域链命名为 Scope

Scope = [AO].concat([[Scope]]);

我们来举个例子吧,对《JavaScript 权威指南》 中的例子进行修改

var scope = 'global scope';
function checkscope() {
  var scope = 'local scope';
  return scope;
}
checkscope();

来看看执行过程吧

checkscope.[[scope]] = [
  globalContext.VO
]
ECStack = [checkscopeContext, globalContext];
checkscopeContext = {
    AO: {
      arguments: {
        length: 0
      },
      scope: undefined
    },
    Scope: checkscope.[[scope]]
}
checkscopeContext = {
  AO: {
    arguments: {
      length: 0,
    },
    scope: undefined,
  },
  Scope: [AO, [[Scope]]],
};
checkscopeContext = {
  AO: {
    arguments: {
      length: 0,
    },
    scope: 'local scope',
  },
  Scope: [AO, [[Scope]]],
};
ECStack = [globalContext];