JavaScript引擎底层的工作原理

有没有想过浏览器如何读取和运行JS代码? 这看起来很神奇,我们可以通过浏览器提供的控制台来了解背后的一些原理。

在Chrome中打开浏览器控制台,然后查看Sources这栏,在右侧可以到一个 Call Stack 盒子。
在这里插入图片描述
JS 引擎是一个可以编译和解释我们的JS代码强大的组件。 最受欢迎的JS 引擎是V8,由 Google Chrome 和 Node.j s使用,SpiderMonkey 用于Firefox,以及Safari/WebKit使用的 JavaScriptCore。

虽然现在 JS 引擎不是帮我们处理全面的工作。但是每个引擎中都有一些较小的组件为我们做繁琐的的工作。

其中一个组件是调用堆栈(Call Stack),与全局内存和执行上下文一起运行我们的代码。

Js 引擎和全局内存(Global Memory)
JavaScript 是编译语言同时也是解释语言。信不信由你,JS 引擎在执行代码之前只需要几微秒就能编译代码。

这听起来很神奇,对吧?这种神奇的功能称为JIT(即时编译)。这个是一个很大的话题,一本书都不足以描述JIT是如何工作的。但现在,我们午饭可以跳过编译背后的理论,将重点放在执行阶段,尽管如此,这仍然很有趣。

考虑以下代码:

var num = 2;
function pow(num) {
   
    return num * num;
}

如果问你如何在浏览器中处理上述代码? 你会说些什么? 你可能会说“浏览器读取代码”或“浏览器执行代码”。

现实比这更微妙。首先,读取这段代码的不是浏览器,是JS引擎。JS引擎读取代码,一旦遇到第一行,就会将几个引用放入全局内存。

**全局内存(也称为堆)**JS引擎保存变量和函数声明的地方。因此,回到上面示例,当 JS引擎读取上面的代码时,全局内存中放入了两个绑定。
即使示例只有变量和函数,也要考虑你的JS代码在更大的环境中运行:在浏览器中或在Node.js中。 在这些环境中,有许多预定义的函数和变量,称为全局变量。

上例中,没有执行任何操作,但是如果我们像这样运行函数会怎么样呢:

var num = 2;
function pow(num) {
   
    return num * num;
}
pow(num);

现在事情变得有趣了。当函数被调用时,JavaScript引擎会为全局执行上下文和调用栈腾出空间。
JS引擎:它们是如何工作的? 全局执行上下文和调用堆栈
刚刚了解了 JS引擎如何读取变量和函数声明,它们最终被放入了全局内存(堆)中。

但现在我们执行了一个JS函数,JS引擎必须处理它。怎么做?每个JS引擎中都有一个基本组件,叫调用堆栈。

调用堆栈是一个堆栈数据结构:这意味着元素可以从顶部进入,但如果它们上面有一些元素,它们就不能离开,JS 函数就是这样的。

一旦执行,如果其他函数仍然被阻塞,它们就不能离开调用堆栈。请注意,这个有助于你理解“JavaScript是单线程的”这句话。

回到我们的例子,当函数被调用时,JS引擎将该函数推入调用堆栈在这里插入图片描述
同时,JS 引擎还分配了一个全局执行上下文,这是运行JS代码的全局环境,如下所示在这里插入图片描述
想象全局执行上下文是一个海洋,其中全局函数像鱼一样游动,多美好! 但现实远非那么简单, 如果我函数有一些嵌套变量或一个或多个内部函数怎么办?

即使是像下面这样的简单变化,JS引擎也会创建一个本地执行上下文:

var num = 2;
function pow(num) {
   
    var fixed = 89;
    return num * num;
}
pow(num);

注意,我在pow函数中添加了一个名为fixed的变量。在这种情况下,pow函数中会创建一个本地执行上下文,fixed 变量被放入pow函数中的本地执行上下文中。

对于嵌套函数的每个嵌套函数,引擎都会创建更多的本地执行上下文。

JavaScript 是单线程和其他有趣的故事
JavaScript是单线程的,因为只有一个调用堆栈处理我们的函数。也就是说,如果有其他函数等待执行,函数就不能离开调用堆栈。

在处理同步代码时,这不是问题。例如,两个数字之间的和是同步的,以微秒为单位。但如果涉及异步的时候,怎么办呢?

幸运的是,默认情况下JS引擎是异步的。即使它一次执行一个函数,也有一种方法可以让外部(如:浏览器)执行速度较慢的函数,稍后探讨这个主题。

当浏览器加载某些JS代码时,JS引擎会逐行读取并执行以下步骤:

将变量和函数的声明放入全局内存(堆)中
将函数的调用放入调用堆栈
创建全局执行上下文,在其中执行全局函数
创建多个本地执行上下文(如果有内部变量或嵌套函数)
到目前为止,对JS引擎的同步机制有了基本的了解。 在接下来的部分中,讲讲 JS 异步工作原理。

异步JS,回调队列和事件循环
全局内存(堆),执行上下文和调用堆栈解释了同步 JS 代码在浏览器中的运行方式。 然而,我们遗漏了一些东西,当有一些异步函数运行时会发生什么?

请记住,调用堆栈一次可以执行一个函数,甚至一个阻塞函数也可以直接冻结浏览器。 幸运的是JavaScript引擎是聪明的,并且在浏览器的帮助下可以解决问题。

当我们运行一个异步函数时,浏览器接受该函数并运行它。考虑如下代码:

setTimeout(callback, 10000);
function callback(){
   
    console.log('hello timer!');
}

setTimeout 大家都知道得用得很多次了,但你可能不知道它不是内置的JS函数。 也就是说,当JS 出现,语言中没有内置的setTimeout。

setTimeout浏览器API( Browser API)的一部分,它是浏览器免费提供给我们的一组方便的工具。这在实战中意味着什么?由于setTimeout是一个浏览器的一个Api,函数由浏览器直接运行(它会在调用堆栈中出现一会儿,但会立即删除)。

10秒后,浏览器接受我们传入的回调函数并将其移动到回调队列(Callback Queu)中。。考虑以下代码

var num = 2;
function pow(num) {
   
    return num * num;
}
pow(num);
setTimeout(
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值