Please enable Javascript to view the contents

从头学习js-5-作用域链

 ·  ☕ 2 分钟

这个系列是我读冴羽老师博客的感悟,
加入了个人的解读和练习题的解答

上篇说到,当javascript代码执行一段可执行代码(executable code)时,会创建对应的执行上下文(execution context)

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

  • 变量对象(Variable object, VO)
  • 作用域链(Scope chain)
  • this

本文主要讲执行上下文中的作用域链,注意本章将会把之前的内容穿起来很重要


在之前第2篇作用域和第4篇变量对象中有提到,
当查到变量时首先从当前上下文的变量对象中查到,
如果没有找到会从父级(词法静态层面上的)执行上下文的变量对象中查找,
一直找到全局变量对象(全局对象)

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


函数创建

在第2篇中提到js中函数的作用域在函数定义的时候就决定了

这是因为函数有一个内部属性`[[scope]]``, 当函数创建的时候会把函数的父变量对象保存到其中,但不是完整的作用链

如:

1
2
3
4
5
function foo() {
  function bar() {
    ...
  }
}

此时函数的各自作用域为:

1
2
3
4
5
6
7
foo.[[scope]] = [
  globalContext.VO
];
bar.[[scope]] = [
  fooContext.AO,
  globalContext.VO
];

函数激活

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

1
Scope = [AO].concat([[scope]])

此时作用域链创建完毕

总结事例

例子:

1
2
3
4
5
6
var scope = 'global scope';
function checkscope() {
  var scope2 = 'local scope';
  return scope2;
}
checkscope();

函数执行上下文中作用域和变量对象创建过程如下:
1.checkscope函数被创建,保存作用域到内部属性[[scope]]

1
2
3
checkscope.[[scope]] = [
  globalContext.VO
]

2.执行checkscope函数,创建checkscope函数执行上下文,checkscope函数执行上下文压入执行上下栈

1
2
3
4
ECStack = [
  checkscopeContext,
  globalContext
];

3.checkscope函数进入准备阶段
3.1 复制函数内部属性[[scope]]到函数执行上文中

1
2
3
checkscopeContext = {
  Scope: checkscope.[[scope]]
}

3.2 用函数的arguments属性创建活动对象,随后初始化活动对象,加入形参、函数声明、变量声明

1
2
3
4
5
6
7
8
9
checkscopeContext = {
  AO: {
    arguments: {
      length: 0,
    },
    scope2: undefined
  },
  Scope: checkscope.[[scope]]
}

3.3 将活动对象压入作用域顶端

1
2
3
4
5
6
7
8
9
checkscopeContext = {
  AO: {
    arguments: {
      length: 0,
    },
    scope2: undefined
  },
  Scope: [AO, [[scope]]]
}

准备工作完毕

4.开始执行checkscope函数,随着函数执行,修改AO的属性值

1
2
3
4
5
6
7
8
9
checkscopeContext = {
  AO: {
    arguments: {
      length: 0,
    },
    scope2: 'local scope' 
  },
  Scope: [AO, [[scope]]]
}

5.查找到scope2的值,返回后函数执行完毕,checkscope函数上下文从执行上下文中弹出

1
2
3
ECStack = [
  globalContext
]
分享

Llane00
作者
Llane00
Web Developer