说到JAVAScript的运行原理,自然绕不开JS引擎,运行上下文,单线程,事件循环,事件驱动,回调函数等概念 。本文主要参考文章[1,2] 。
为了更好的理解JavaScript如何工作的,首先要理解以下几个概念 。
- JS Engine(JS引擎)
- Runtime(运行上下文)
- Call Stack (调用栈)
- Event Loop(事件循环)
- Callback (回调)
简单来说,JS引擎主要是对JS代码进行词法、语法等分析,通过编译器将代码编译成可执行的机器码让计算机去执行 。
目前最流行的JS引擎非V8莫属了,Chrome浏览器和Node.js采用的引擎就是V8引擎 。引擎的结构可以简单由下图表示:
文章插图
就如JVM虚拟机一样,JS引擎中也有堆(Memory Heap)和栈(Call Stack)的概念 。
- 栈 。用来存储方法调用的地方,以及基础数据类型(如var a = 1)也是存储在栈里面的,会随着方法调用结束而自动销毁掉(入栈-->方法调用后-->出栈) 。
- 堆 。JS引擎中给对象分配的内存空间是放在堆中的 。如var foo = {name: 'foo'} 那么这个foo所指向的对象是存储在堆中的 。
关于闭包的情况,就涉及到Captured Variables 。我们知道Local Variables是最简单的情形,是直接存储在栈中的 。而Captured Variables是对于存在闭包情况和with,try catch情况的变量 。
function foo () { var x; // local variables var y; // captured variable, bar中引用了y function bar () { // bar 中的context会capture变量y use(y); } return bar;}复制代码如上述情况,变量y存在与bar()的闭包中,因此y是captured variable,是存储在堆中的 。
2.RunTime
JS在浏览器中可以调用浏览器提供的API,如window对象,DOM相关API等 。这些接口并不是由V8引擎提供的,是存在与浏览器当中的 。因此简单来说,对于这些相关的外部接口,可以在运行时供JS调用,以及JS的事件循环(Event Loop)和事件队列(Callback Queue),把这些称为RunTime 。有些地方也把JS所用到的core lib核心库也看作RunTime的一部分 。
文章插图
同样,在Node.js中,可以把Node的各种库提供的API称为RunTime 。所以可以这么理解,Chrome和Node.js都采用相同的V8引擎,但拥有不同的运行环境(RunTime Environments)[4] 。
3.Call Stack
JS被设计为单线程运行的,这是因为JS主要用来实现很多交互相关的操作,如DOM相关操作,如果是多线程会造成复杂的同步问题 。因此JS自诞生以来就是单线程的,而且主线程都是用来进行界面相关的渲染操作 (为什么说是主线程,因为html5 提供了Web Worker,独立的一个后台JS,用来处理一些耗时数据操作 。因为不会修改相关DOM及页面元素,因此不影响页面性能),如果有阻塞产生会导致浏览器卡死 。
如果一个递归调用没有终止条件,是一个死循环的话,会导致调用栈内存不够而溢出,如:
function foo() { foo();}foo();复制代码例子中foo函数循环调用其本身,且没有终止条件,浏览器控制台输出调用栈达到最大调用次数 。
文章插图
JS线程如果遇到比较耗时操作,如读取文件,AJAX请求操作怎么办?这里JS用到了Callback回调函数来处理 。
对于Call%20Stack中的每个方法调用,都会形成它自己的一个执行上下文Execution%20Context,关于执行上下文的详细阐述请看这篇文章
4.Event%20Loop%20&%20Callback
JS通过回调的方式,异步处理耗时的任务 。一个简单的例子:
var%20result%20=%20ajax('...');console.log(result);复制代码此时并不会得到result的值,result是undefined 。这是因为ajax的调用是异步的,当前线程并不会等到ajax请求到结果后才执行console.log语句 。而是调用ajax后请求的操作交给回调函数,自己是立刻返回 。正确的写法应该是:
ajax('...',%20function(result)%20{%20console.log(result);})复制代码此时才能正确输出请求返回的结果 。
JS引擎其实并不提供异步的支持,异步支持主要依赖于运行环境(浏览器或Node.js) 。
So,%20for%20example,%20when%20your%20JavaScript%20program%20makes%20an%20Ajax%20request%20to%20fetch%20some%20data%20from%20the%20server,%20you%20set%20up%20the%20“response”%20code%20in%20a%20function%20(the%20“callback”),%20and%20the%20JS%20Engine%20tells%20the%20hosting%20environment:%20“Hey,%20I’m%20going%20to%20suspend%20execution%20for%20now,%20but%20whenever%20you%20finish%20with%20that%20network%20request,%20and%20you%20have%20some%20data,%20please%20call%20this%20function%20back.”
推荐阅读
- VLAN工作原理之ACCESS:接收到带VLAN的报文如何处理
- 深度原理学习–Redis集群
- “减速玻璃”这个忽悠我们几十年的黑科技,原理是啥?一次讲明白
- 三星|三星手机曝重大漏洞 运行Android 9至12的所有机型危险了
- redis分布式锁 Redlock原理分析
- javascript操作元素
- JavaScript字符串处理方法
- 自旋锁的自旋是什么意思,底层原理又是什么?
- 断点续传原理
- Java中常见的服务器