详释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 相关文章推荐
优化网页之快速的呈现我们的网页
Jun 29 Javascript
jQuery与ExtJS之选择实例分析
Aug 19 Javascript
JavaScript插件化开发教程(五)
Feb 01 Javascript
javascript如何操作HTML下拉列表标签
Aug 20 Javascript
javascript HTML5 canvas实现打砖块游戏
Jun 18 Javascript
IE8利用自带的setCapture和releaseCapture解决iframe的拖拽事件方法
Oct 25 Javascript
bootstrap+jQuery 实现下拉菜单中复选框全选和全不选效果
Jun 12 jQuery
zepto.js 实时监听输入框的方法
Dec 04 Javascript
vue基于两个计算属性实现选中和全选功能示例
Feb 08 Javascript
微信小程序进入广告实现代码实例
Sep 19 Javascript
js实现前端界面导航栏下拉列表
Aug 27 Javascript
vscode自定义vue模板的实现
Jan 27 Vue.js
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
DIY实用性框形天线
2021/03/02 无线电
linux命令之调试工具strace的深入分析
2013/06/03 PHP
php实现Mysql简易操作类
2015/10/11 PHP
Zend Framework分发器用法示例
2016/12/11 PHP
基于jquery的一个浮动框(扩展性比较好 )
2010/08/27 Javascript
js获得地址栏?问号后参数的方法
2013/08/08 Javascript
javaScript array(数组)使用字符串作为数组下标的方法
2013/11/19 Javascript
jQuery获得内容和属性示例代码
2014/01/16 Javascript
js中top的作用深入剖析
2014/03/04 Javascript
JavaScript获取页面上被选中文字的方法技巧
2015/03/13 Javascript
如何用jQuery实现ASP.NET GridView折叠伸展效果
2015/09/26 Javascript
javascript 初学教程及五子棋小程序的简单实现
2017/07/04 Javascript
vue.js移动端app之上拉加载以及下拉刷新实战
2017/09/11 Javascript
详解Immutable及 React 中实践
2018/03/01 Javascript
p5.js入门教程之图片加载
2018/03/20 Javascript
vue-for循环嵌套操作示例
2019/01/28 Javascript
JavaScript禁止右击保存图片,禁止拖拽图片的实现代码
2020/04/28 Javascript
Python专用方法与迭代机制实例分析
2014/09/15 Python
Python的“二维”字典 (two-dimension dictionary)定义与实现方法
2016/04/27 Python
Python基于pillow判断图片完整性的方法
2016/09/18 Python
Python做文本按行去重的实现方法
2016/10/19 Python
Python优先队列实现方法示例
2017/09/21 Python
Python3中类、模块、错误与异常、文件的简易教程
2017/11/20 Python
Python基于xlrd模块操作Excel的方法示例
2018/06/21 Python
详解python websocket获取实时数据的几种常见链接方式
2019/07/01 Python
简单了解python数组的基本操作
2019/11/26 Python
利用python进行文件操作
2020/12/04 Python
adidas爱尔兰官方网站:阿迪达斯运动鞋和运动服
2019/11/01 全球购物
设计师大码女装:11 Honoré
2020/05/03 全球购物
shell程序如何生命变量?shell变量是弱变量吗?
2014/11/10 面试题
工程造价自荐信
2013/10/09 职场文书
同学会邀请书大全
2014/01/12 职场文书
省级优秀毕业生主要事迹
2014/05/29 职场文书
2015年设计师个人工作总结
2015/04/25 职场文书
python如何查找列表中元素的位置
2022/05/30 Python
Redis基本数据类型String常用操作命令
2022/06/01 Redis