Javascript中什么是调用上下文
我猜你不知道这个答案。
编程语言最基本的组成部分是什么?
变量和函数对吗?每个人都可以学习这些模块。
但是什么是基础呢?
在成为中级(甚至高级)JavaScript开发人员之前,您应该掌握哪些JavaScript的知识?有很多:作用域、闭包、回调、原型等等。
但是在深入了解这些概念之前,你至少应该理解JavaScript引擎是如何工作的。
在这篇文章中,我们将遍历每个JavaScript引擎的两个基本部分:执行上下文和调用堆栈。(不要害怕。这比你想象的容易)
你将学到什么
- JavaScript引擎是如何工作的
- JavaScript中的执行上下文
- 调用堆栈是什么
- 全局执行上下文与本地执行上下文的区别
JavaScript如何运行代码?
JavaScript如何运行代码?
如果你是高级开发人员,你可能已经知道答案了。
如果你是初学者,我们将共同努力。
事实上,JavaScript内部结构并不容易。
但我保证你能学会它们。
当你学会它们的时候,你会觉得自己更有力量,更聪明。
通过查看JavaScript内部功能,您将成为一个更好的JavaScript开发人员,即使您不能掌握每一个细节。
现在,看看下面的代码:
var num = 2; function pow(num) { return num * num; }
看起来不难!
现在告诉我:你认为浏览器会评估那个代码的顺序是什么?
换句话说,如果你是浏览器,你将如何阅读该代码?
听起来很简单。
大多数人认为“是的,浏览器执行函数POW并返回结果,然后它分配2到NUM。”
你想知道我学生的答案吗?
从上到下
浏览器将从函数POW开始,计算num * num
JS引擎将逐行运行代码(种类)
我一直期待着。
我说过几年前同样的事情。
在下一节中,你会发现那些看似简单的代码行背后的机制。
JavaScript引擎
你只想成为一个普通的JavaScript开发者吗?
我敢打赌你不会的。
如果你想在JavaScript面试中留下好印象,你至少应该知道JavaScript是如何运行你的代码的。
(还有一些其他的东西,我不会在这里涉及)。
不要急于这些概念。
一天之内你什么也学不到。这需要时间。
好消息?我要让每个人都明白这一点(至少我会尝试)。
要理解JavaScript如何运行代码,我们应该遇到第一件可怕的事情:
执行上下文。
JavaScript中的执行上下文是什么?
每次在浏览器(或节点)中运行JavaScript时,引擎都会经历一系列的步骤。
其中一个步骤涉及创建全局执行上下文。
等一下瓦伦蒂诺,引擎是什么?
也就是说,JavaScript引擎是运行JavaScript代码的“引擎”。
现在有两个著名的JavaScript引擎:Google V8 和 SpiderMonkey(蜘蛛猴)
V8是谷歌的开源JavaScript引擎,用于谷歌浏览器和NoDE.js。
SpiderMonkey是Mozilla的JavaScript引擎,用于Firefox。
到目前为止,我们有JavaScript引擎和执行上下文。
现在是时候了解他们是如何合作的。
它是如何工作的?
每次运行一些JavaScript代码时,引擎都会创建一个全局执行上下文。
执行上下文是用来描述JavaScript代码运行的环境的一个花哨词。我感觉到你很难想象这些抽象的东西。现在将全局执行上下文视为一个框:
来看一下我们的代码
var num = 2; function pow(num) { return num * num; }
引擎是如何读取代码的?
这里有一个简化的版本:
引擎:一号线。有个变量!酷。让我们把它存储在全局内存中。
引擎:三号线。我看到一个函数声明。酷。让我们把它存储在全局内存中!
引擎:看来我完了。
如果我再问你一次:浏览器怎么看下面的代码,你会怎么说?
是的,从上到下,但是…
正如你所看到的引擎没有运行函数POW!
它是函数声明,而不是函数调用。
上面的代码将在全局内存中存储的一些值中转换:函数声明和变量。
全局存储器?
瓦伦蒂诺,我已经被执行上下文弄糊涂了,现在你把全局内存扔给我了?
是的,我是。
让我们看看全局内存是什么。
全局存储器
JavaScript引擎也有一个全局内存。
全局内存包含全局变量和函数声明供以后使用。
如果您阅读Kyle Simpson的“作用域和闭包”,您可能会发现全局内存与全局作用域的概念重叠。
事实上,它们是一样的东西。
我飞了10000英尺高,这是有原因的。
这些都是很难理解的概念。
但现在你不必担心。
我想让你们理解我们的两个重要问题。
当JavaScript引擎运行您的代码时,它创建:
- 全局执行上下文
- 全局内存(也称为全局作用域或全局变量环境)
一切都清楚了吗?
如果我是你的话,我会:
写一些JavaScript代码
逐个解析代码,因为您是引擎
在执行期间对全局执行上下文和全局内存进行图形化表示。
你可以把练习写在纸上或者用一个原型工具。
对于我的小例子,图片看起来是这样的:
在下一节中,我们将看到另一个可怕的事情:调用堆栈。
调用堆栈是什么?
你清楚执行上下文、全局内存和JavaScript引擎是如何结合在一起的吗?
如果不花时间复习前一节。
我们将在我们的谜题中引入另一个部分:调用堆栈。
让我们先回顾一下JavaScript引擎运行代码时会发生什么。
它创造:
- 全局执行上下文
- 全局存储器
除此之外,在我们的例子中没有发生任何事情:
var num = 2; function pow(num) { return num * num; }
代码是纯值的分配。
让我们再向前走一步。如果调用函数,会发生什么?
var num = 2; function pow(num) { return num * num; } var res = pow(num);
有趣的问题。
在JavaScript中调用函数的行为使引擎请求帮助。
这个帮助来自JavaScript引擎的一个朋友:调用堆栈。
这听起来可能不是很明显,但是JavaScript引擎需要跟踪正在发生的事情。
它依赖于调用堆栈。
JavaScript中的调用堆栈是什么?
调用堆栈就像程序当前执行的日志。
实际上,它是一个数据结构:一个堆栈。
调用堆栈究竟是如何工作的?
毫不奇怪,它有两种方法:PUP和POP。
推动是将某物放入堆栈中的行为。
也就是说,当您在JavaScript中运行函数时,引擎将该函数推送到调用堆栈中。
每个函数调用都被推送到调用堆栈中。
第一个被推的东西是main()(或GULL()),JavaScript程序的主要线程。
现在,前面的图片看起来是这样的:
在另一端输出的是从堆栈中移除某物的行为。
当函数结束时,函数从调用堆栈中输出。
我们的调用堆栈看起来如下:
现在呢?你已经准备好从那里掌握每一个JavaScript概念了。
我不是开玩笑的。
但哪里没有完成!转到下一节!
本地执行上下文
到目前为止一切似乎都很清楚。
我们错过什么了吗?
我们知道JavaScript引擎创建全局执行上下文和全局内存。
然后,当您在代码中调用函数时:
- JavaScript引擎请求帮助
- 这个帮助来自JavaScript引擎的一个朋友:调用堆栈
- 调用堆栈跟踪代码中调用什么函数的轨迹。
当你在JavaScript中运行函数时,还有另外一件事要发生。
首先,函数出现在全局执行上下文中。
然后,另一个迷你上下文出现在函数旁边:
这个新的小框被称为本地执行上下文。
什么??
如果您注意到,在前面的图片中,全局内存中出现了一个新变量:var RES.
变量RES首先具有未定义的值。
然后,当POW出现在全局执行上下文中时,函数执行并且RES获取其返回值。
在执行阶段,创建本地执行上下文以保存局部变量。
多么强大的概念。
记住这一点:
了解全局和本地执行上下文是掌握作用域和闭包的关键。
总结
你能相信4行代码后面是什么吗?
JavaScript引擎创建执行上下文、全局内存和调用堆栈。
但是,一旦调用函数,引擎就创建具有本地内存的本地执行上下文。
在这篇文章的末尾,你应该能够理解当你运行一些JavaScript代码时会发生什么。
经常被忽略的是,JavaScript内部构件总是被新开发人员视为是错误的东西。
然而,它们是掌握先进JavaScript概念的关键。
如果您学习执行上下文、全局内存和调用堆栈,那么作用域、闭包、回调和其他内容将是轻而易举的。
特别是,理解调用堆栈是最重要的。
一旦你可视化它,所有的JavaScript将开始变得有意义:你最终会理解为什么JavaScript是异步的,以及为什么我们需要回调。
你知道4行JavaScript代码后面是什么吗?
现在你知道了。
谢谢阅读!