learn icon indicating copy to clipboard operation
learn copied to clipboard

JavaScript 深入之执行上下文

Open yangtao2o opened this issue 4 years ago • 0 comments

执行上下文

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

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

分析第一段代码

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

1.执行全局代码,创建全局执行上下文,全局上下文被压入执行上下文栈

ECStack = [globalContext];

2.全局上下文初始化

globalContext = {
  VO: [global],
  Scope: [globalContext.VO],
  this: globalContext.VO
};

初始化的同时,checkscope 函数被创建,保存作用域链到函数的内部属性[[scope]]

checkscope.[[scope]] = [globalContext.VO]

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

ECStack = [checkscope, globalContext];

4.checkscope 函数执行上下文初始化:

  1. 复制函数 [[scope]] 属性创建作用域链,
  2. 用 arguments 创建活动对象,
  3. 初始化活动对象,即加入形参、函数声明、变量声明,
  4. 将活动对象压入 checkscope 作用域链顶端。

同时 f 函数被创建,保存作用域链到 f 函数的内部属性[[scope]].

checkscopeContext = {
  AO: {
    arguments: {
      length: 0
    },
    scope: undefined,
    f: reference to function f(){}
  },
  Scope: [AO, globalContext.VO],
  this: undefined
}

5.执行 f 函数,创建 f 函数执行上下文,f 函数执行上下文被压入执行上下文栈

ECStack = [fContext, checkscope, globalContext];

6.f 函数执行上下文初始化

fContext = {
  AO: {
    arguments: {
      length: 0
    }
  },
  Scope: [AO, checkscopeContext.AO, globalContext.VO],
  this: undefined
};

7.f 函数执行,沿着作用域链查找 scope 值,返回 scope 值,这里之所以能够访问到scope

8.f 函数执行完毕,f 函数上下文从执行上下文栈中弹出

9.checkscope 函数执行完毕,checkscope 执行上下文从执行上下文栈中弹出

ECStack = [globalContext];

分析第二段代码

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

1.执行全局代码,创建全局执行上下文,全局上下文被压入执行上下文栈,并初始化全局上下文

ECStack = [globalContext];
globalContext = {
  VO: [global],
  Scope: [globalContext.VO],
  this: globalContext.VO
};

初始化的同时,checkscope 函数被创建,保存作用域链到函数的内部属性[[scope]]

checkscope.[[scope]] = [globalContext.VO];

2.执行 checkscope 函数,创建 checkscope 函数执行上下文,checkscope 函数执行上下文被压入执行上下文栈,并初始化函数上下文

ECStack = [checkscopeContext, globalContext];
checkscopeContext = {
  AO: {
    arguments: {
      length: 0
    },
    scope: undefined,
    f: reference to function f(){}
  },
  Scope: [AO, globalContext.VO],
  this: undefined
}

同时 f 函数被创建,保存作用域链到 f 函数的内部属性[[scope]]

f.[[scope]] = [AO, checkscopeContext.AO, globalContext.VO];

3.checkscope 函数执行完毕,checkscope 执行上下文从执行上下文栈中弹出

ECStack = [globalContext];

4.执行 f 函数,创建 f 函数执行上下文,并压入执行上下文栈,将其初始化

ECStack = [fContext;, globalContext];
fContext = {
  AO: {
    arguments: {
      length: 0
    }
  },
  Scope: [AO, checkscopeContext.AO, globalContext.VO],
  this: undefined
};

5.f 函数执行,沿着作用域链查找 scope 值,返回 scope 值。正是因为 checkscope 函数执行上下文初始化时,f 函数同时被创建,保存作用域链到 f 函数的内部属性[[scope]],所以即使checkscope函数执行完毕,被弹出执行上下文栈,但是checkscopeContext.AO 依然存在于 f 函数维护的[scope]]

fContext = {
    Scope: [AO, checkscopeContext.AO, globalContext.VO],
}

所以,闭包的概念产生了,定义:

  • 即使创建它的上下文已经销毁,它仍然存在(比如,内部函数从父函数中返回)
  • 在代码中引用了自由变量

6.f 函数执行完毕,f 函数上下文从执行上下文栈中弹出

ECStack = [globalContext];

原文链接:JavaScript 深入之执行上下文

yangtao2o avatar Apr 01 '20 04:04 yangtao2o