JavaScript执行上下文-从编译到执行(一)

26 篇文章 1 订阅

对于大多数人来说,js是神秘的,它有许多唯一的特点

你可能听过如下术语:

  • 提升
  • 作用域和作用域链
  • 闭包
  • this

它们都有一些“怪异”的行为,在js中是唯一的

理解这些概念的关键是执行上下文,我希望这篇文章提供一个不同的角度来理解javascript,而不是从MDN上复制一些准确但模糊的信息。

不要误会,我也会从MDN上阅读一些相关信息,然后,我更相信理解比去记住这些信息更加有用。

两步:编译和执行

当一个js代码片段运行时,我们会讨论它的两个步骤:编译和执行

这个看起来很简单,然后,js所有的神秘都藏在这两步中

当js被编译时会发生什么?一个执行上下文会被创建,当执行上下文准备好后,执行阶段开始,所有的可执行代码一行一行地被执行。

执行上下文由以下几个部分组成,为了理解它,我们主要关注它如下四个部分

  • 变量环境(Variable environment)
  • 词法环境(Lexical environment)
  • 关联外部(outer)
  • this

在这篇文章中,变量环境是主角,我们会暂时忽略其它部分。

当来到执行阶段,浏览器会一行一行地运行js代码,与此同时,当一行代码完成运行,执行上下文会被更新。

 

变量和变量环境

我们以一个例子开始

当这个js代码片段被执行,第一步是进行编译,在编译阶段,执行上下文被创建。

与此同时,变量''apple''被声明,值为undefined,并且被存储在变量环境中

编译阶段结束,执行阶段开始

当第一行代码被执行,变量“apple”被赋值为数字10,在同一时刻变量环境被更新。

第二行代码执行,打印语句开始在它的变量环境中查找“apple”变量,并且它找到“apple”变量,在控制台打印10

整个程序结束,整个执行上下文被移出(移出执行栈)

从这个例子中,我们可以看出如下几个关键点

  • 一个变量赋值语句实际是上被分成两步
  • 编译阶段主要关心变量的声明
  • 执行阶段执行变量的赋值和剩下的代码

提升,变量环境中的一个技巧

这个代码片段与之前的代码有一点不同,它在变量“apple”声明之前打印了它

我们都知道这将会打印undefined,但是,让我们从执行上下文的角度来一步一步来看它

在编译阶段,第一行代码被跳过,因为它不涉及变量声明,然后“apple”变量在编译到第二行代码时被创建在环境变量里。编译阶段结束。

在第一行,打印语句console开始在它的变量环境中查找“apple”变量,此刻,变量“apple”的值为undefined,所以控制台打印undefined。

然后第二行代码被执行,并且变量“apple”的值被更新为10,最后整个程序结束。

我们称这个过程为提升,因为变量“apple”像是被提升到程序的顶部。用下面的伪代码来模拟提升的影响

然而,从执行上下文的角度来看,这里没有提升。任何东西都没有被提升,发生这种现象是因为变量在编译的阶段被声明。提升命名是基于这个结果之上的。

提升与函数

函数有点不同因为我们有两种选择来声明函数

声明showName函数时发生赋值,相反,声明showNumber函数时没有赋值发生

有趣的是我们可以看到控制台打印“Hey, show a number”,然后接下来抛出一个错误“showName is not a function.”,让我们重新来看看两步发生了什么?

在编译阶段,showNumber是一个声明,所以在此刻它是被存储起来的

当设计到showName函数时,它被赋值为undefined,因为它是一个赋值语句

showName函数块直到执行阶段是没有被赋值的

如果你第二行代码打印showName的值,而不是去执行它,控制台将会打印undefined,undefined不是一个函数,所以这个错误信息会显示“showName is not a function”。

在这里提一下另一件错误的事,函数部分在我的图中描述得并不准确,函数体应该是存放在一个叫做HEAP的地方,而不是存放在环境变量。

当调用一个函数,浏览器会在HEAP(堆空间)中查找函数体而不是在环境变量中

为了保持简单,我将会把堆放到一旁,在本文继续将函数体放在环境变量的图片来描述它们

两个特殊例子来理解编译技巧

第一个例子是命名冲突

我们知道,如果我们使用相同的变量名字,后一个将会覆盖前一个

你知道如下代码将会打印什么吗?

控制台将会打印“I’m a declaration”,尽管这个变量之后再次被声明。第二个showNumber变量没有覆盖第一个

当一个函数和一个变量有相同的名字时,这个变量声明将会在编译阶段被忽略,换句话说,函数声明有更高的优先权。

这是一个很容易被忽略的陷阱,所以我们应该避免使用相同变量名。

接下来看另一个例子

在这里,条件语句if里是一个假值,所以if代码块是不会运行的

如果你运行这段代码,控制台将会打印undefined,而不是报错“apple is not defined”,在这个例子中,变量“apple”在编译阶段仍然被声明在变量环境中。

结论:

  • javascript运行分两个阶段:编译和执行
  • 编译阶段一个执行上下文被创建,由变量环境和其他部分组成
  • 变量声明是在编译阶段,赋值是在执行阶段。

原文链接https://medium.com/@cabulous/javascript-execution-context-part-1-from-compiling-to-execution-84c11c0660f5

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值