专栏首页京程一灯JS the Hardcore: 执行上下文(Execution Context)

JS the Hardcore: 执行上下文(Execution Context)

先看个例子:

console.log(a)  console.log(foo())  var a = 'hello world'function foo() {  
    console.log('foo')
}

估计大部分人早就对这种问题了如指掌了,输出结果也是脱口而出:

undefined  'foo'  

请解释一下原因? 这不就是常说的 Hoisting 吗? 代码在执行的时候,其实是这个样子的:

function foo() {  
  console.log('foo')
}var aconsole.log(a)  console.log(foo())

a = 'hello world'  

是的,这样解释也可以,但是不够准确。如果仅仅理解到这个层面,而不把这里面涉及到的「执行上下文」(Execution Context, 下面简称 EC)这个概念弄明白,还是不能「知其所以然」。

首先需要澄清的一点是,像 C 和 Java 这类所谓的「静态语言」需要编译后才能运行,但其实 Javascript 一样也是有编译过程的, 只是处于一些限制,编译过程特别快(以微秒计),以至于感觉上 Javascript 代码好像并没有经过编译。

当需要向别人描述一件事情的经过时,往往需要把事情的前因后果以及与其相关的场景预先说明一下。 类似,EC 就是一段代码涉及到的场景,在代码运行之前,js engine 会做一些变量内存分配,代码上下文关联的准备工作,这就是 EC。 EC 是 Javascript 引擎实现的一个内部机制,不能在代码中直接访问到。

对编程语言来说,给变量赋值,获取变量的值,是需要解决的基本问题,Javascript 当然也不例外。 EC 中会给变量分配一个存储空间,与之对应的数据结构称作 enviroment。

当程序流程需要从当前的 EC 进入另外一个和当前上下文无关的代码片段时,会创建一个新的 EC,并被推入栈中。这就是「执行上下文栈」(Execution Context Stack),可以看作是 调用栈的镜像。由于所有的 js 代码都存在于全局环境中,所以首先会创建 「全局执行上下文」(Global Execution Context),除此之外,js 中每一次的函数调用也会生成 EC,所以栈底肯定会是 全局执行上下文。

如果把 EC 视为作一个抽象对象,那这个对象包含了代码相关的 this、enviroment(存储标识符包括变量声明、函数声明、函数表达式的数据结构)和一个指向外部 enviroment 的指针。

Global Execution Context

    Global Execution Context = {        global object,
        this: global object,
        outer environment: null,
        enviroment: {            // all the identifiers
            variable,            function expression,            function declaration,
        },
    }

Function Execution Context

    Execution Context = {        this: some value,
        outer environment: outer lexcial environment,
        enviroment: {            // all the identifiers
            parameter,            arguments,
            variable,            function expression,            function declaration,
        },
    }

在 EC 创建之后,js engine 会开始顺序执行代码。

/** 01 **/ var x = 'hello world'/** 02 **//** 03 **/ function foo() {/** 04 **/   var y = 'hellow foo'/** 05 **/   console.log(y)/** 06 **/ }/** 07 **//** 08 **/ console.log(x)/** 09 **/ foo()

代码执行前,首先创建全局执行上下文并设置为 「当前执行上下文」(running execution context)

    Execution Context = {        global object,
        this: global object,
        outer environment: null,
        enviroment: {
            x: undefined,
            foo,
            bar,
        },
    }

之后,顺序执行代码,执行到 ln 01 时,给变量 x 赋值,

    Global Execution Context = {
        global object,        this: global object,
        outer environment: null,
        enviroment: {
            x: 'hello world',
            foo,
            bar,
        },
    }

代码执行到 ln 08 时,需要去 enviroment 中寻找 x 的值,此时为 'hello world'

代码执行到 ln 09 时,因为是对函数的调用,会创建一个新的执行上下文,并置为「当前执行上下文」:

    foo Execution Context = {        this: window,
        outer environment: Global.environment,
        enviroment: {            arguments,
            y: undefined,
            bar,
        },
    }

然后代码继续执行,到 ln 04 时,有对 y 的赋值操作,此时

    foo Execution Context = {        this: window,
        outer environment: Global.environment,
        enviroment: {            arguments,
            y: 'hello foo',
        },
    }

代码执行到 ln 05 有对 y 的取值操作,y 值在当前的 enviroment 中为 'hellow foo'

EC 栈的情况,可以参照下图「调用栈」(Call Stack)的状态:

到这里就基本上把执行上下文的一些基本概念讲完了,其中涉及到的一些内容目前不理解没有关系,后面会慢慢解释。

本文分享自微信公众号 - 京程一灯(jingchengyideng),作者:糖伴西红柿

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2017-08-01

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • JavaScript 私有类字段和 TypeScript 私有修饰符

    在本文中,我们将对 JavaScript 私有类字段进行一些说明,并了解它们与 TypeScript 私有修饰符的区别。

    疯狂的技术宅
  • 理解 JavaScript 中的作用域

    作用域是 JavaScript 中的一个重要而又模糊的概念。只有正确使用 JavaScript 作用域,才能使用优秀的设计模式,帮助你规避副作用。本文中,我们将...

    疯狂的技术宅
  • ES 基础 —— 执行上下文

    Execution Context(执行上下文)是 ECMA-262 标准中定义的一个抽象概念,用于同 Executable Code(可执行代码)进行区分。

    疯狂的技术宅
  • 理解javascript闭包前,先理解作用域链

    1. 全局作用域(Global Scope)   在代码中任何地方都能访问到的对象拥有全局作用域,一般来说以下几种情形拥有全局作用域:   (1)最外层函数和...

    前朝楚水
  • 函数随笔

      (3) 局部命名空间:函数内部定义的,当调用函数是才会起作用,随着函数的结束而结束.

    用户2398817
  • JavaScript之作用域和闭包

    keyWords
  • JS入门难点解析3-作用域

    (注1:如果有问题欢迎留言探讨,一起学习!转载请注明出处,喜欢可以点个赞哦!) (注2:更多内容请查看我的目录。)

    love丁酥酥
  • Python学习笔记:命名空间和作用域

    在Python中,任何“东西”都是一个对象。当我们赋值整数给变量时,例如x = 1,我们告诉Python在引用x时,意味着Python指向整数类型对象1,以便对...

    fanjy
  • 基础知识 | 每日一练(42)

    士人有百折不回之真心,才有万变不穷之妙用。立业建功,事事要从实地着脚,若少慕声闻,便成伪果;讲道修德,念念要从虚处立基,若稍计功效,便落尘情。 ...

    闫小林
  • 稳扎稳打JavaScript(一)——作用域链内存模型

    几个概念 在开始之前,先了解几个概念。 1.1. 作用域 作用域是指当前正在执行的代码能够访问到变量的范围; 每个函数都有各自的作用域,存储函数所有的局部变量...

    大闲人柴毛毛

扫码关注云+社区

领取腾讯云代金券

玩转腾讯云 有奖征文活动