我们大概经常能听到“执行环境”、“作用域”、“原型(链)”、“执行上下文”等内容,它们都在描述什么?

JS代码的运行

我们知道了js是弱类型语言,在运行时才确定变量类型。js引擎在执行js代码时,也会从上到下进行 词法分析语法分析语义分析 等处理,并在代码解析完成后生成AST(抽象语法树),最终根据AST生成CPU可以执行的机器码并执行。

除此之外,JS引擎在执行代码时还会进行其它处理,如 V8 中还有两个阶段:

  • 编译阶段:该阶段会进行执行上下文的创建,包括创建变量对象(VO)(此时会被初始化为undefined)、建立作用域链、确定 this 指向等。每进入一个不同的运行环境。V8 都会创建一个新的执行上下文。
  • 执行阶段:将编译阶段中创建的执行上下文压入调用栈,并成为正在运行的执行上下文。代码执行结束后,将其弹出调用栈。(这里有一个VO - AO的过程:JavaScript对变量赋值时变量被用到,此时变量对象会转为活动对象,转换后的活动对象才可被访问)

这就引出了两个概念:“执行上下文” 和 “作用域链”。


JavaScript执行上下文

由上面我们可以知道:当js代码执行一段可执行代码时,会创建对应的执行上下文。
首先,js中可执行代码对应着有一个概念:“执行环境” —— 全局环境、函数环境 和 eval
其次,对于每个执行上下文,都有三个重要属性:

  • 变量对象(即“VO”)
  • 作用域链
  • this

我们来看两段代码:

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()();

它们会打印什么?

为什么?答案是它们的执行上下文栈不一样!

什么是“执行上下文栈”?
当执行一个可执行代码时,就会提前做准备工作,这里的“准备工作”,专业的说法就是“执行上下文”。但随着可执行代码如函数的增多,如何管理那么多的执行上下文呢?所以JS引擎创建了执行上下文栈的概念。
我们完全可以用数组去模拟其行为(栈底永远有一个全局执行上下文globalContext)

我们定义一个EStack,首先

EStack=[globalContext];

然后来模拟第一段代码:

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

而第二段代码是这样的:

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

究其原因,你可能需要先研究一下“闭包”的概念了!

这里顺便说下“在前端模块化”中怎么实现“长时间保存数据”?
缓存?不。闭包!


JavaScript作用域和作用域链

首先,作用域是指程序中定义变量的区域。作用域规定了如何查找变量,也就是确定了当前执行代码对变量的访问权限。
作用域有两种:静态作用域动态作用域
JS采用的静态作用域,也叫“词法作用域”。函数的作用域在函数定义的时候就确定了。

由上,词法作用域中的变量,在编译过程中会产生一个确定的作用范围。这个作用范围即“当前的执行上下文”。在ES5后我们用“词法环境”替代作用域来描述该执行上下文。词法环境由两个成员组成:

  • 自身词法环境记录:用于记录自身词法环境中的变量对象
  • 外部词法环境引用:用于记录外层词法环境中存在的引用

我们依然来看一个例子:

var value=1;
function foo(){console.log(value);
}
function bar(){var value=2;foo();
}
bar();

回看上面的定义,该打印什么?

让我们分析下执行过程:
执行foo()函数,先从foo函数内部查找是否有局部变量value。如果没有,就根据定义时的位置,查找上面一层的代码,也就是value=1.所以结果会打印1。

这里面当然不是如此简单能概括的,你可以从执行上下文的角度分析一下。

建立作用域链

上面我们说了词法环境(作用域)的两个组成。再结合执行上下文,我们不难发现:通过外部词法环境的引用,作用域可以顺着栈层层拓展,建立起从当前环境向外延伸的一条链式结构。

再来看一个例子:

function foo(){console.dir(bar);var a=1;function bar(){a=2;}
}
console.dir(foo);
foo();

由静态作用域,全局函数foo创建了一个自身对象的 [[scope]] 属性

foo[[scope]]=[globalContext];

而当我们执行foo()时,也会先后进入foo函数的定义期和执行期。在foo函数的定义期时,函数bar的 [[scope]] 将会包含全局内置scope和foo的内置scope

bar[[scope]]=[fooContext,globalContext];

这证明了这一点:“JS会通过外部词法环境引用来创建变量对象的一个作用域链,从而保证对执行环境有权访问的变量和函数的有序访问。”

让我们再回头看看执行上下文中的那道题,在前面我们说了它们有什么不同,这里说下为什么它们相同地打印了“local scope”:还是那句话“JS采用的是词法作用域,函数的作用域取决于函数创建的位置” —— JS函数的执行用到了作用域链,这个作用域链是在函数定义的时候创建的。嵌套的函数 f() 定义在这个作用域链里,其中的变量scope一定是指局部变量,不管何时何地执行 f() ,这种绑定在执行 f() 时依然有效。

基于作用域链的变量查询

当某个变量无法在自身词法环境记录中找到时,可以根据外部词法环境引用向外层进行寻找,直到最外层的词法环境中外部词法环境引用为null
与此相似的是“对象中基于原型链的查找”:

  • 原型:每一个JS对象(null 除外)在创建时就会与另一个对象关联,这个对象就是我们说的原型。每一个对象都会从原型中“继承”属性。
  • 当读取实例的属性时,如果找不到,就会查找与对象关联的原型中的属性,如果还找不到,就去找原型的原型,一直到最顶层(__proto__为null)为止

它们的区别也显而易见:原型链是通过 prototype 属性建立对象继承的链接;而作用域链是指内部函数能访问到外部函数的闭包。不管直接还是间接,所有函数的作用域链最终都链接到全局上下文。

深入JavaScript之JS引擎如何执行JS代码相关推荐

  1. Js引擎解析执行 阅读笔记

    Js引擎解析执行 阅读笔记 一篇阅读笔记 http://km.oa.com/group/2178/articles/show/145691?kmref=search&from_page=1&a ...

  2. 好程序员web前端分享JS引擎的执行机制

    好程序员web前端分享JS引擎的执行机制,请先着重牢记两点!JS是单线程语言. JS的EventLoop是JS的执行机制.深入了解JS的执行,就等于深入了解JS里的eventloop. 1.灵魂三问: ...

  3. 异步加载js文件并执行js方法:实现异步处理网页的复杂效果

    异步加载js文件并执行js方法:实现异步处理网页的复杂效果 有这么一个场景,当你的网页页面效果过多就会造成了打开页面的速度变得缓慢,长时间处于加载的状态,这样的效果通常会让用户感到不友好,通常的处理方 ...

  4. JavaScript中的JS引擎的执行机制

    为什么JavaScript是单线程? Javascript引擎是单线程机制,首先我们要了解Javascript语言为什么是单线程 JavaScript的主要用途主要是用户互动,和操作DOM.如果Jav ...

  5. java rhino 运行 js_java 脚本引擎Rhino执行js代码和文件

    测试js脚本引擎 public class helloworld { public static void main(String[] args) throws Exception { //获得脚本引 ...

  6. JS引擎线程的执行过程的三个阶段

    浏览器首先按顺序加载由<script>标签分割的js代码块,加载js代码块完毕后,立刻进入以下三个阶段,然后再按顺序查找下一个代码块,再继续执行以下三个阶段,无论是外部脚本文件(不异步加载 ...

  7. JS引擎线程的执行过程的三个阶段(二)

    继续 JS引擎线程的执行过程的三个阶段(一) 内容, 如下: 三. 执行阶段 1. 网页的线程 永远只有JS引擎线程在执行JS脚本程序,其他三个线程只负责将满足触发条件的处理函数推进事件队列,等待JS ...

  8. js引擎执行js代码的过程

    js引擎执行js代码的过程 html解析 浏览器下载网页时,浏览器内核解析html.当遇到script标签时,下载js代码并将js代码以流的形式传递给js引擎让js引擎进行js代码的解析. 语义语法分 ...

  9. python利用浏览器执行js_浏览器如何执行JS

    作为JS系列的第一篇,内容当然是浏览器如何执行一段JS啦. 首先通过浏览器篇我们可以得知,JS是在渲染进程里的JS引擎线程执行的.在此之后还要了解几个概念,编译器(Compiler).解释器(Inte ...

  10. 浏览器内核、排版引擎、js引擎

    [定义] 浏览器最重要或者说核心的部分是"Rendering Engine",可大概译为"渲染引擎",不过我们一般习惯将之称为"浏览器内核" ...

最新文章

  1. java 远程共享_【原创】(扫盲)远程共享对象SharedObject的用法
  2. Java进程与线程的区别
  3. python模糊查找文件夹名字_python实现在目录中查找指定文件的方法
  4. Windows下Caffe的学习与应用(二)——优化自己训练的模型(fine-tuning)
  5. 【深度学习】编写同时在PyTorch和Tensorflow上工作的代码
  6. 软件包管理 之 Fedora / Redhat 软件包管理指南
  7. 设计模式(二)模板方法模式
  8. ase支持 urp不_urp管线的自学hlsl之路 第一篇 序言
  9. 4.2 算法之数论 185 反正切函数的应用 python
  10. Vue和后台交互的方式
  11. 缓冲电路/延时上电电路
  12. 腾讯会议html代码,电脑端使用腾讯会议的加入会议步骤
  13. 数据库之操作MySQL
  14. 纪念碑谷背后的故事:不差钱!8人团队研发十月
  15. Unity Failed to resolve project template:Failed to decompress
  16. {电脑救助站}常用知识4
  17. 计算机网络路由器作用是什么,路由器的作用是什么?
  18. 数据库的行格式ROW_FORMAT
  19. 泡泡机器人原创专栏-Cartographer】Cartographer理论及实现浅析
  20. %格式化和format格式化--python

热门文章

  1. 3S基础知识:在VC++中嵌入MapX的集成二次开发
  2. 关于WAP技术的介绍
  3. centos是什么linux操作系统,Linux 操作系统之CentOS的介绍
  4. python执行adb命令_Python脚本之ADB命令(一)
  5. Linux开发板移植minicom
  6. SQL入门经典-思维导图
  7. esp ghost引导_ghost做uefi+gpt 需要什么cmd命令修复引导?
  8. 智能生成标题 易语言代码
  9. 华为交换机通用配置方式方法
  10. 中源幼儿园收费管理系统 pdf软件