JavaScript 系列 - 作用域

77 阅读3分钟

解析过程

可执行代码

  • 全局执行代码,在执行所有代码前,解析创建全局执行上下文
  • 函数执行代码,执行函数前,解析创建函数执行上下文
  • eval 执行代码,运行于当前执行上下文中

边解析边执行

执行上下文 EC (Execution Context)

变量对象 VO (Variable object)

  • 函数的所有形参
  • 函数声明
  • 变量声明

全局上下文中的变量对象

  • 全局上下文中的变量对象在执行所有代码之前创建
  • 全局上下文中的变量对象一直存在,直到程序结束
  • 全局上下文中的变量对象保存了全局上下文中所有的变量和函数声明
  • 全局变量对象位于作用域链的顶端

函数上下文中的变量对象 AO

arguments,代码解析阶段赋值

作用域链 SC (Scope chain)

SEC.png

  • 前端,当前执行上下文的变量对象
  • 末尾,全局执行上下文的变量对象

动态创建

先创建全局上下文

  • 创建全局变量对象
  • 将全局变量对象放入作用域链的顶端
  • 全局上下文中的 [[Scope]] 属性值放到变量对象中

函数创建对应的执行上下文

  • 复制函数的 [[Scope]] 属性用来创建作用域链
  • arguments 创建活动对象
  • 活动对象压入作用域链顶端

扩展作用域链

  • 在作用域链的前端临时增加一个变量对象

    代码执行后被移除

  • try-catch 语句中的 catch

    包含了被抛出的错误对象的声明,然后将这个变量对象压入当前上下文作用域链中

  • with 语句

    指定的对象压入当前上下文作用域链中

1.png

2.png

var number = 1;
compare1(5, 10);
function compare1(value1, value2) {
  inner();
  if (value1 < value2) {
    return -1;
  } else if (value1 > value2) {
    return 1;
  } else {
    return 0;
  }
  function inner() {
    var a = 1;
  }
}
globalContext[[Scope]] = [globalContext.VO];
compare[[Scope]] = [globalContext.VO];
compareContext = {
  Scope: compare[[Scope]],
};
checkScopeContext = {
  AO: {},
  Scope: [AO, [[Scope]]],
};
inner[[Scope]] = [checkScopeContext.AO, globalContext.VO];
// try-catch 语句中的 catch 块
function outer() {
  var number = 1;
  with (location) {
  }
}
// 将 location 对象添加到当前上下文作用域链的顶端

this

  • this 指向最后一次调用这个方法的对象
  • this 到对象的绑定发生在函数调用的时候
  • 方法调用模式,this 指向调用对象
  • 函数调用模式,this 指向 window
  • 构造器调用模式,this 指向新创建的对象
  • apply 、 call 和 bind 调用模式,指定 this

规范类型

详细介绍

ECMAScript中将类型分为:语言类型和规范类型。

语言类型可以理解为就是我们平常代码中使用的类型:undefined,null,number,string,boolean,object 等等。

规范类型只存在于规范中,它的作用是为了更好的描述代码底层的行为逻辑,并不存在于实际的js代码中。规范类型包括:Reference,List,Completion,Property Descriptor,Property Identifier,Lexical Environment,Environment Record

this 指向与 Reference 类型密切相关

Reference

构成
  • base value
    • undefined
    • Object
    • Boolean
    • String
    • Number
    • environment record
  • referenced name 属性的名称
  • strict reference 代码是否处于严格环境下,值为 boolean 类型
方法
  • GetBase

    返回 referencebase value

  • IsPropertyReference

    如果 base value 是一个对象,就返回 true

  • GetValue

    Reference 类型获取对应值

this 指向

  • 计算 MemberExpression 的结果赋值给 ref

    • 原始表达式
    • 函数表达式
    • 属性访问表达式
    • new 表达式
  • 判断 ref 是不是一个 Reference 类型

    • 如果 ref 是 Reference,并且 IsPropertyReference(ref) 是 true, 那么 this 的值为 GetBase(ref)
    • 如果 ref 是 Reference,并且 base value 值是 Environment Record, 那么this的值为 ImplicitThisValue(ref)
    • 如果 ref 不是 Reference,那么 this 的值为 undefined
var value = 1;
var foo = {
  value: 2,
  bar: function () {
    return this.value;
  },
};
// MemberExpression foo.bar
// var Reference = {
//   base: foo,
//   name: "bar",
//   strict: false,
// };
// IsPropertyReference(ref) === foo === object === true;
// this = GetBase(ref) = foo
console.log(foo.bar()); // 2
// 类似一
console.log((foo.bar)()); // 2
// this window
console.log((foo.bar = foo.bar)()); // 1
// this window
console.log((false || foo.bar)()); // 1
// this window
console.log((foo.bar, foo.bar)()); // 1

执行上下文栈 ECS(Execution context stack)