深入学习js之——作用域链

栏目: JavaScript · 发布时间: 5年前

内容简介:作用域是每种计算机语言最重要的基础之一,因此要想深入的学习JavaScript,作用域和作用域链就是个绕不开的话题。在对于每个执行上下文,都有三个重要属性:

开篇

作用域是每种计算机语言最重要的基础之一,因此要想深入的学习JavaScript,作用域和作用域链就是个绕不开的话题。

《深入学习js之—-执行上下文栈》 中我们提到过,当JavaScript代码执行一段可执行代码(executable code)时,会创建对应的执行上下文(execution context)。

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

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

今天重点聊聊作用域链。

作用域

细说作用域链之前,我们首先来聊聊作用域,简单的说,作用域就是变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期。

在JavaScript中,变量的作用域有 全局作用域局部作用域 两种(局部作用域又称为 函数作用域 )。

作用域链

代码在当查找变量的时候,会先从当前上下文的 变量对象 中查找,如果没有找到,就会从父级(词法层面上的父级)执行上下文的变量对象中查找,一直找到全局上下文的变量对象,也就是全局对象。

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

下面,让我们以一个函数的创建和激活两个时期来讲解作用域链是如何创建和变化的。

函数创建

《深入学习js之——词法作用域和动态作用域》 中讲到,函数的作用域在函数定义的时候就决定了——即JavaScript采用的是 静态作用域

这是因为函数有一个内部属性 [[scope]] ,当函数创建的时候,就会保存所有父变量对象到其中,你可以理解 [[scope]] 就是所有父变量对象的层级链,但是注意: [[scope]] 并不代表完整的作用域链!

举个例子:

function foo(){
  function bar(){
      ...
  }
}

函数创建时,各自的 [[scope]] 为:

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

bar.[[scope]] = [
    fooContext.AO,
    globalContext.VO
];

函数激活

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

这时候执行上下文的作用域链,我们命名为 Scope:

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

至此,作用域链创建完毕。

通过例子深刻理解

以下面的例子为例,结合着之前讲的 变量对象执行上下文栈 ,我们来总结一下函数执行上下文中作用域链和变量对象的创建过程:

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

执行过程如下:

1.checkscope 函数被创建,保存作用域链到 内部属性 [[scope]]

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

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

ECStack = [
  checkscopeContext,
  globalContext
];

3.checkscope 函数并不立刻执行,开始做准备工作,第一步:复制函数 [[scope]] 属性创建作用域链;

checkscopeContext = {
    Scope: checkscope.[[scope]],
}

4.第二步:用 arguments 创建活动对象,随后初始化活动对象,加入形参、函数声明、变量声明;

checkscopeContext = {
  AO: {
    arguments: {
      length: 0
    },
    scope2: undefined
  },
  Scope: checkscope.[[scope]],
}

5.第三步:将活动对象压入 checkscope 作用域链顶端;

checkscopeContext = {
  AO: {
    arguments: {
        length: 0
    },
    scope2: undefined
  },
  Scope: [AO, [[Scope]]]
}

6.准备工作做完,开始执行函数,随着函数的执行,修改 AO 的属性值;

checkscopeContext = {
  AO: {
    arguments: {
        length: 0
    },
    scope2: 'local scope'
  },
  Scope: [AO, [[Scope]]]
}

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

ECStack = [
    globalContext
];

参考:

《JavaScript深入之作用域链》

《深入了解JavaScript,从作用域链开始》

欢迎添加我的个人微信讨论技术和个体成长。

深入学习js之——作用域链

欢迎关注我的个人微信公众号——指尖的宇宙,更多优质思考干货

深入学习js之——作用域链


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Big Java Late Objects

Big Java Late Objects

Horstmann, Cay S. / 2012-2 / 896.00元

The introductory programming course is difficult. Many students fail to succeed or have trouble in the course because they don't understand the material and do not practice programming sufficiently. ......一起来看看 《Big Java Late Objects》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试