详释JavaScript执行环境与执行栈


Posted in Javascript onApril 02, 2019

执行环境

执行环境 ( 也称"执行上下文" ) 可以说是 JavaScript 最重要的一个概念。那么执行环境到底是什么呢?一句话就可以概括:代码 ( 包括函数 ) 执行时所需要的所有信息就是执行环境。由于 ES 历经多个版本,所以执行环境的标准也一直在变,下面列出了三个主要的版本内容:

ES3 标准中的执行环境

  • scope:作用域,如果有作用域嵌套的情况就称作"作用域链"。
  • variable object:变量对象,用于存储标识符的特殊对象。
  • this value:this 值。

*标识符:包括变量、函数名、属性名和函数的参数。

ES5 标准中的执行环境

  • variable environment:变量环境,当声明变量时使用。
  • lexical environment:词法环境,当获取标识符值时使用。
  • this value:this 值。

ES6 标准中的执行环境

  • variable environment:变量环境,当声明变量时使用。
  • lexical environment:词法环境,当获取标识符值或者 this 值时使用。

*在 ES6 中,执行环境中实际增加了不少内容,我们这里只介绍了普通函数执行时所需要的内容。

执行栈

当打开网页或浏览器时,宿主环境(1)会将代码传递给引擎(2)去执行,引擎首先会创建一个全局执行环境。全局环境中的代码自上而下有顺序的执行,当遇到一个函数时,函数的环境被创建,函数中的代码开始执行;而在函数执行之后,控制权又返还给之前的环境。ES 这种类似于" 栈 "(3)的控制机制,称为执行栈。

(1) 宿主环境:浏览器或者 Node 环境。
(2) 引擎:从头到尾负责整个 JavaScript 代码的编译及执行过程。
(3) 栈:一种遵循" 后进先出 "原则的有序数据集合,可以简单理解为使用 push() 和 pop() 操作数组。

例子:

console.log(1);

function pFn() {
 console.log(2);
 (function cFn() {
 console.log(3);
 }());
 console.log(4);
}
pFn();

console.log(5);
//输出:1 2 3 4 5

示意图:

详释JavaScript执行环境与执行栈

我们可以通过浏览器,直观的看一下执行栈的形式:

详释JavaScript执行环境与执行栈

编译原理

我们知道,执行环境中有很多非常有用的" 工具 “,这些” 工具 “会协助引擎完成整个函数的执行工作。例如,ES3 标准中的作用域,它会协助引擎查找当前环境中所有标识符的定义的位置;变量对象,帮助引擎保存环境中的变量和函数。当然,这些工作大部分情况下发生在代码执行前的几微秒之内,称之为” 编译阶段 "。JavaScript 的整个编译阶段比较复杂,一般会经历词法分析、语法分析、代码生成、性能优化等步骤,这里不做深入讨论。

下面我们举例说明,看看当函数 fn 执行的时候,引擎是如何工作的:

var b=1;
function fn(){
 var a = 1;
 return a+b;
}
fn();

1、首先,遇到 var a,引擎会询问作用域是否已经有一个该名称的变量存在于同一个作用域中。如果存在,引擎会忽略该声明,继续进行编译;很显然不存在,所以引擎会在当前作用域中声明一个新的变量,并命名为 a ( 此时还没有赋值,默认为 undefined )。

2、第二步,又遇到 a,引擎会首先询问作用域,在当前的作用域中是否存在一个叫作 a 的变量,很显然存在,所以引擎就会使用这个变量;遇到 b,引擎对作用域做出同样的询问,很显然不存在,所以引擎会到外层嵌套的作用域中继续查找,在全局作用域找到了该变量,引擎就会将 1 赋值给变量 b 。

3、经过以上两步,函数 fn 环境中出现的所有标识符的值已经基本锁定,那么引擎就会立即自上而下开始执行代码。为变量 a 赋值 1,计算 1+1 的值并返回它。

4、最后一步,函数 fn 的环境销毁,退出执行栈,将控制权返还给全局环境。

变量提升的原因

在编译阶段,引擎会声明变量和函数,但不会对变量进行赋值,这主要是出于对性能的考虑。变量被声明,但是不一定会在后面使用到,如果没有使用却赋了值,只是白白浪费内存而已。上面例子中的全局变量 b ,在函数 fn 没有执行之前,也不会赋值,直到函数中使用了这个变量,才不得不去加载数字 1。简单的说,var a 这段代码发生在编译阶段,而 =1 这段代码会根据实际情况,发生在执行阶段,这也就是" 变量提升 "的原因。另外需要注意的是,函数声明的是整个函数体( 因为函数声明不存在赋值操作),而且优先级高于同名的变量。

例子1:

console.log(fn()); //输出:1
console.log(n); //输出:undefined

function fn() {
 return 1;
}
var n = 2;

由于声明发生在赋值的前面,上面例子1的代码可以理解为下面的形式:

function fn() {
 return 1;
}
var n;

console.log(fn()); //输出:1
console.log(n); //输出:undefined

n = 2;

由于函数声明优先级高,因此同名变量声明会被忽略,上面例子2的代码可以理解为下面的形式:

function fn() {
 console.log(1);
}

//由于函数声明优先级高,因此这个变量声明会被忽略
//var fn;

fn(); //输出:1

fn = function() {
 console.log(2);
}

*变量提升并非物理意义上的顺序改变,代码执行的顺序还是按照你书写代码时的顺序在执行。只是由于,变量声明发生在代码的编译阶段,而变量赋值却发生在代码的执行阶段,时间上的差异导致了这种现象。

运行时流程图

综合以上的内容,JavaScript 的运行时流程图如下:

详释JavaScript执行环境与执行栈

Javascript 相关文章推荐
alixixi runcode.asp的代码不错的应用
Aug 08 Javascript
Bootstrap每天必学之表格
Nov 23 Javascript
JavaScript的ExtJS框架中数面板TreePanel的使用实例解析
May 21 Javascript
JS Ajax请求如何防止重复提交
Jun 13 Javascript
AngularJS入门教程之ng-class 指令用法
Aug 01 Javascript
概述一个页面从输入URL到页面加载完的过程
Dec 16 Javascript
jQuery UI仿淘宝搜索下拉列表功能
Jan 10 Javascript
bootstrap paginator分页插件的两种使用方式实例详解
Nov 14 Javascript
angular 服务的单例模式(依赖注入模式下)详解
Oct 22 Javascript
JS实现的杨辉三角【帕斯卡三角形】算法示例
Feb 26 Javascript
微信小程序获取用户信息的两种方法wx.getUserInfo与open-data实例分析
May 03 Javascript
浅谈JavaScript中你可能不知道URL构造函数的属性
Jul 13 Javascript
mongodb初始化并使用node.js实现mongodb操作封装方法
Apr 02 #Javascript
koa大型web项目中使用路由装饰器的方法示例
Apr 02 #Javascript
vue中v-text / v-html使用实例代码详解
Apr 02 #Javascript
Seajs源码详解分析
Apr 02 #Javascript
使用mixins实现elementUI表单全局验证的解决方法
Apr 02 #Javascript
移动端自适应flexible.js的使用方法(不用三大框架,仅写一个单html页面使用)推荐
Apr 02 #Javascript
基于vue实现web端超大数据量表格的卡顿解决
Apr 02 #Javascript
You might like
ajax php 实现写入数据库
2009/09/02 PHP
php 在文件指定行插入数据的代码
2010/05/08 PHP
php中函数的形参与实参的问题说明
2010/09/01 PHP
Array对象方法参考
2006/10/03 Javascript
Javascript实现仿WebQQ界面的“浮云”兼容 IE7以上版本及FF
2011/04/27 Javascript
一个关于javascript匿名函数的问题分析
2012/03/30 Javascript
5秒后跳转效果(setInterval/SetTimeOut)
2013/05/03 Javascript
js实现点击链接后延迟3秒再跳转的方法
2015/06/05 Javascript
使用Angular.js开发的注意事项
2016/10/19 Javascript
javascript阻止事件冒泡和浏览器的默认行为
2017/01/21 Javascript
canvas实现弧形可拖动进度条效果
2017/05/11 Javascript
基于Bootstrap表单验证功能
2017/11/17 Javascript
jQuery中复合选择器简单用法示例
2018/03/31 jQuery
React props和state属性的具体使用方法
2018/04/12 Javascript
微信小程序项目实践之验证码倒计时功能
2018/07/18 Javascript
js+audio实现音乐播放器
2020/09/13 Javascript
three.js中多线程的使用及性能测试详解
2021/01/07 Javascript
[31:55]完美世界DOTA2联赛循环赛 IO vs GXR BO2第一场 11.04
2020/11/05 DOTA
python基础教程之udp端口扫描
2014/02/10 Python
python使用Tkinter显示网络图片的方法
2015/04/24 Python
使用Python的Flask框架构建大型Web应用程序的结构示例
2016/06/04 Python
Python使用psutil获取进程信息的例子
2019/12/17 Python
Ubuntu16.04安装python3.6.5步骤详解
2020/01/10 Python
python词云库wordCloud使用方法详解(解决中文乱码)
2020/02/17 Python
python爬虫开发之PyQuery模块详细使用方法与实例全解
2020/03/09 Python
如何利用python发送邮件
2020/09/26 Python
Python GUI之tkinter窗口视窗教程大集合(推荐)
2020/10/20 Python
详解Python中@staticmethod和@classmethod区别及使用示例代码
2020/12/14 Python
德国baby-markt婴儿用品瑞士网站:baby-markt.ch
2017/06/09 全球购物
司机检讨书
2014/02/13 职场文书
微笑面对生活演讲稿
2014/05/13 职场文书
学校法制宣传月活动总结
2014/07/03 职场文书
年终晚会活动方案
2014/08/21 职场文书
学习经验交流会策划书
2015/11/02 职场文书
《角的初步认识》教学反思
2016/02/17 职场文书
2019升学宴主持词范本5篇
2019/10/09 职场文书