顺序执行?

如果要问到 JavaScript 代码执行顺序的话,想必写过 JavaScript 的开发者都会有个直观的印象,那就是顺序执行,毕竟:

var foo = function () {

    console.log('foo1');

}

foo();  // foo1

var foo = function () {

    console.log('foo2');

}

foo(); // foo2

然而去看这段代码:

function foo() {

    console.log('foo1');

}

foo();  // foo2

function foo() {

    console.log('foo2');

}

foo(); // foo2

打印的结果却是两个 foo2

刷过面试题的都知道这是因为 JavaScript 引擎并非一行一行地分析和执行程序,而是一段一段地分析执行。当执行一段代码的时候,会进行一个“准备工作”,比如第一个例子中的变量提升,和第二个例子中的函数提升。

但是本文真正想让大家思考的是:这个“一段一段”中的“段”究竟是怎么划分的呢?

到底JavaScript引擎遇到一段怎样的代码时才会做“准备工作”呢?

可执行代码

这就要说到 JavaScript 的可执行代码(executable code)的类型有哪些了?

其实很简单,就三种,全局代码、函数代码、eval代码。

举个例子,当执行到一个函数的时候,就会进行准备工作,这里的“准备工作”,让我们用个更专业一点的说法,就叫做"执行上下文(execution context)"。

执行上下文栈

接下来问题来了,我们写的函数多了去了,如何管理创建的那么多执行上下文呢?

所以 JavaScript 引擎创建了执行上下文栈(Execution context stack,ECS)来管理执行上下文

为了模拟执行上下文栈的行为,让我们定义执行上下文栈是一个数组:

ECStack = [];

试想当 JavaScript 开始要解释执行代码的时候,最先遇到的就是全局代码,所以初始化的时候首先就会向执行上下文栈压入一个全局执行上下文,我们用 globalContext 表示它,并且只有当整个应用程序结束的时候,ECStack 才会被清空,所以程序结束之前, ECStack 最底部永远有个 globalContext:

ECStack = [
globalContext
];

现在 JavaScript 遇到下面的这段代码了:

function fun3() {
console.log('fun3')
} function fun2() {
fun3();
} function fun1() {
fun2();
} fun1();

当执行一个函数的时候,就会创建一个执行上下文,并且压入执行上下文栈,当函数执行完毕的时候,就会将函数的执行上下文从栈中弹出。知道了这样的工作原理,让我们来看看如何处理上面这段代码:

// 伪代码

// fun1()
ECStack.push(<fun1> functionContext); // fun1中竟然调用了fun2,还要创建fun2的执行上下文
ECStack.push(<fun2> functionContext); // 擦,fun2还调用了fun3!
ECStack.push(<fun3> functionContext); // fun3执行完毕
ECStack.pop(); // fun2执行完毕
ECStack.pop(); // fun1执行完毕
ECStack.pop(); // javascript接着执行下面的代码,但是ECStack底层永远有个globalContext

解答思考题

好啦,现在我们已经了解了执行上下文栈是如何处理执行上下文的,所以让我们看看上篇文章《JavaScript深入之词法作用域和动态作用域》最后的问题:

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

两段代码执行的结果一样,但是两段代码究竟有哪些不同呢?

答案就是执行上下文栈的变化不一样。

让我们模拟第一段代码:

ECStack.push(<checkscope> functionContext);
ECStack.push(<f> functionContext);
ECStack.pop();
ECStack.pop();

让我们模拟第二段代码:

ECStack.push(<checkscope> functionContext);
ECStack.pop();
ECStack.push(<f> functionContext);
ECStack.pop();

是不是有些不同呢?

当然了,这样概括的回答执行上下文栈的变化不同,是不是依然有一种意犹未尽的感觉呢,为了更详细讲解两个函数执行上的区别,我们需要探究一下执行上下文到底包含了哪些内容,所以欢迎阅读下一篇《JavaScript深入之变量对象》。

原文网址:https://github.com/mqyqingfeng/Blog/issues/4

[JavaScript深入系列]JavaScript深入之执行上下文栈(转载)的更多相关文章

  1. 【进阶1-2期】JavaScript深入之执行上下文栈和变量对象(转)

    这是我在公众号(高级前端进阶)看到的文章,现在做笔记 https://mp.weixin.qq.com/s/hZIpnkKqdQgQnK1BcrH6Nw 阅读笔记 JS是单线程的语言,执行顺序肯定是顺 ...

  2. js执行上下文栈和变量对象

    JavaScript执行上下文栈和变量对象 JS是单线程的语言,执行顺序肯定是顺序执行,但是JS 引擎并不是一行一行地分析和执行程序,而是一段一段地分析执行,会先进行编译阶段然后才是执行阶段. 例子一 ...

  3. 深入理解javascript原型和闭包(11)——执行上下文栈

    继续上文的内容. 执行全局代码时,会产生一个执行上下文环境,每次调用函数都又会产生执行上下文环境.当函数调用完成时,这个上下文环境以及其中的数据都会被消除,再重新回到全局上下文环境.处于活动状态的执行 ...

  4. JavaScript深入之执行上下文栈

    如果要问到 javascript 代码执行顺序的话,想必写过javascript的开发者都会有个直观的印象,那就是顺序执行,例如: var foo = function(){ console.log( ...

  5. JavaScript进阶系列05,事件的执行时机, 使用addEventListener为元素同时注册多个事件,事件参数

    本篇体验JavaScript事件的基本面,包括: ■ 事件必须在页面元素加载之后起效■ 点击事件的一个简单例子■ 为元素注册多个点击事件■ 获取事件参数 ■ 跨浏览器事件处理 □ 事件必须在页面元素加 ...

  6. 前端知识体系:JavaScript基础-原型和原型链-理解JavaScript的执行上下文栈,可以应用堆栈信息快速定位问题

    理解JavaScript的执行上下文栈,可以应用堆栈信息快速定位问题(原文文档) 1.什么是执行上下文: 简而言之,执行上下文就是当前JavaScript代码被解析和执行时所在环境的抽象概念,Java ...

  7. js基础梳理-究竟什么是执行上下文栈(执行栈),执行上下文(可执行代码)?

    日常在群里讨论一些概念性的问题,比如变量提升,作用域和闭包相关问题的时候,经常会听一些大佬们给别人解释的时候说执行上下文,调用上下文巴拉巴拉,总有点似懂非懂,不明觉厉的感觉.今天,就对这两个概念梳理一 ...

  8. 【学习笔记】深入理解js原型和闭包(11)——执行上下文栈

    继续上文的内容. 执行全局代码时,会产生一个执行上下文环境,每次调用函数都又会产生执行上下文环境.当函数调用完成时,这个上下文环境以及其中的数据都会被消除,再重新回到全局上下文环境.处于活动状态的执行 ...

  9. JS高阶---执行上下文栈

    大纲: 主体: 注意:*******函数调用时才会产生上下文栈,声明时不会产生********** 顺序: 概念图: 执行上下文栈的顺序---→后进先出 其他概念图: 当前执行的上下文总是在顶部 全局 ...

随机推荐

  1. 1145. Hashing - Average Search Time (25)

    The task of this problem is simple: insert a sequence of distinct positive integers into a hash tabl ...

  2. X-MAS CTF 2018 - Crypto - Hanukkah

    参考链接 https://ctftime.org/task/7321 https://github.com/VoidHack/write-ups/tree/master/X-MAS%20CTF%202 ...

  3. 【Database】Mysql分布式集群学习笔记

    一.sql 的基本操作 (2018年11月29日,笔记) (1)数据库相关操作 创建数据库.查看数据库.删除数据库 #. 创建数据库 create database mytest default ch ...

  4. 程序员要注意!现在是RSS复兴的时候了

    一般来说,现代网络不乏恐怖,从无所不在的网络黑客到所有信息平台,再到各大平台的评论系统.不幸的是,我们建立的这个互联网并没有什么灵丹妙药.但任何人都厌倦了黑箱算法,控制你在网上看到的东西,一直存在但始 ...

  5. python 按多维列表中的某一个元素位进行排序

    import os,re top = os.popen("tasklist") process_list = [] split_r = r"\s+" memor ...

  6. Python---基础---爱因斯坦阶梯问题

    写一个程序,打印出0-100所有奇数 ls = range(0, 101)for i in ls: if i % 2 == 1: print(i)--------------------------- ...

  7. MySQL跑得慢的原因分析

    第一点,硬件太老 硬件我们这里主要从CPU.内存.磁盘三个方面来说下,还有一些因素比如网卡,机房网络等因为文章篇幅关系,就不一一介绍了,以后还有机会可以聊. 首先我们来看下MySQL对CPU的利用特点 ...

  8. LDD3 第15章 内存映射和DMA

    本章内容分为三个部分: 第一部分讲述了mmap系统调用的实现过程.将设备内存直接映射到用户进程的地址空间,尽管不是所有设备都需要,但是能显著的提高设备性能. 如何跨越边界直接访问用户空间的内存页,一些 ...

  9. 2017ICPC沈阳网络赛 HDU 6205 -- card card card(最大子段和)

    card card card Time Limit: 8000/4000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)To ...

  10. 四-1、Cadence Allegro推荐操作方式和视图命令

    第四章:实用命令详解 1.Cadence Allegro推荐操作方式: 激活命令 选择操作对象的属性 设置相关的命令参数 单击对应的对象 结束命令 2.视图命令: