我知道每当我调用我自己编写的函数时,Javascript 都会创建一个执行上下文。例如:
function sum(a, b) {
return a + b;
}
// new execution context will be created here:
sum()
但是,当我调用 setTimeOut() 或 fetch() 等 Web api 函数时会发生什么
最佳答案
是的,所有函数调用都会导致创建执行上下文。
首先介绍一点背景知识,以便后续的解释更容易理解。规范讨论了内部槽,这些是必须实现的抽象属性和方法。它们不暴露在外面,所以你不能直接使用它们,它们用于实现。该规范使用双方括号中的名称,例如 [[Name]] - 指的是名为“name”的内部插槽。
考虑到这一点,我们也 clarify what is a function according to the spec (滚动到6.1.7.2部分的底部):
Table 7 summarizes additional essential internal methods that are supported by objects that may be called as functions. A function object is an object that supports the [[Call]] internal method. A constructor is an object that supports the [[Construct]] internal method. Every object that supports [[Construct]] must support [[Call]]; that is, every constructor must be a function object. Therefore, a constructor may also be referred to as a constructor function or constructor function object.
Table 7然后更抽象地描述了 [[Call]] 和 [[Construct]] 的作用。无论如何,简而言之,它是一个具有内部槽 [[Call]] 的对象。
最后,我们可以看看[[Call]]。引用章节9.2 ECMAScript Function Objects的规范摘自 how [[Call]] should work 章节:
9.2.1 [[Call]] ( thisArgument, argumentsList )
The [[Call]] internal method for an ECMAScript function object F is called with parameters thisArgument and argumentsList, a List of ECMAScript language values. The following steps are taken:
- Assert: F is an ECMAScript function object.
- If F.[[IsClassConstructor]] is true, throw a TypeError exception.
- Let callerContext be the running execution context.
- Let calleeContext be PrepareForOrdinaryCall(F, undefined).
- Assert: calleeContext is now the running execution context.
- Perform OrdinaryCallBindThis(F, calleeContext, thisArgument).
- Let result be OrdinaryCallEvaluateBody(F, argumentsList).
- Remove calleeContext from the execution context stack and restore callerContext as the running execution context.
- If result.[[Type]] is
return
, return NormalCompletion(result.[[Value]]).- ReturnIfAbrupt(result).
- Return NormalCompletion(undefined).
这是完整的部分,它适用于“正常”功能 - 由您或我创建的功能。全套步骤实际上是无关紧要的,我只是为了完整性才将其包括在内。重要的部分是第 4 步。 - 我不会引用 PrepareForOrdinaryCall 的全部内容。因为除了这些之外,还有更多的步骤与我们无关:
- Let calleeContext be a new ECMAScript code execution context.
- Push calleeContext onto the execution context stack; calleeContext is now the running execution context.
- Return calleeContext.
因此,简而言之,该函数获得一个新的运行执行上下文并将其放入堆栈中。好的。然后在[[Call]]的第8步,执行上下文被从堆栈中删除。
<小时/>现在,让我们看一下 9.3 Built-in Function Objects 部分。这些是由环境提供并符合 ECMAScript 规范的函数对象,例如 Object
、Array
、parseInt
等。允许这些内置函数在语言本身中实现(不一定是 JavaScript,请记住 ECMAScript 是标准,因此它将是实现标准的语言)或提供 到环境,这意味着可能用不同的语言实现。不过,规范说:
If a built-in function object is not implemented as an ECMAScript function it must provide [[Call]] and [[Construct]] internal methods that conform to the following definitions:
因此,即使提供了,函数仍然必须提供[[Call]]
槽。和here is the behaviour it should follow :
9.3.1 [[Call]] ( thisArgument, argumentsList )
The [[Call]] internal method for a built-in function object F is called with parameters thisArgument and argumentsList, a List of ECMAScript language values. The following steps are taken:
- Let callerContext be the running execution context.
- If callerContext is not already suspended, suspend callerContext.
- Let calleeContext be a new execution context.
- Set the Function of calleeContext to F.
- Let calleeRealm be F.[[Realm]].
- Set the Realm of calleeContext to calleeRealm.
- Set the ScriptOrModule of calleeContext to F.[[ScriptOrModule]].
- Perform any necessary implementation-defined initialization of calleeContext.
- Push calleeContext onto the execution context stack; calleeContext is now the running execution context.
- Let result be the Completion Record that is the result of evaluating F in a manner that conforms to the specification of F.thisArgument is the this value, argumentsList provides the named parameters, and the NewTarget value is undefined.
- Remove calleeContext from the execution context stack and restore callerContext as the running execution context.
- Return result.
同样,全套步骤是无关紧要的。重要的是 3.、9. 和 11. - 创建新的执行上下文,将其推送到堆栈,最后将其删除。步骤 8. 允许对执行上下文进行任何特定于实现的更改。
<小时/>这些部分规定了运行函数的工作方式。在这两种情况下,都会创建新的执行上下文,将其放入堆栈中,然后在函数完成后将其删除。
为了完整起见,这里是 chapter 8.3 . Execution Contexts如果你想了解更多关于他们的信息。总结一下重要部分,每个线程的任意一点最多有一个正在运行的执行上下文。 正在运行的执行堆栈也被定义为一个 LIFO 结构,当前正在运行的执行上下文位于顶部。
关于javascript - 当我们调用 web api 函数时,javascript 会创建执行上下文吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60892069/