There are four kinds of context in which variable or function declarations would occur: Global, module, function, and block. This article focuses on the function context.
Steps in Specification of Declarations in Function Context
When a function is called in JavaScript, the engine should prepare a new execution context for it. Here are the steps in the specification of how a context is prepared for a function call before its execution and how it is removed when the function call is completed:
[[Call]] ( thisArgument, argumentsList )
- If
F.[[IsClassConstructor]]
istrue
, throw a TypeError exception. - Create a new callee execution context (PrepareForOrdinaryCall ( F, newTarget ))
callerContext
= the running execution context.calleeContext
= a new ECMAScript code execution context.calleeContext.Function
← FcalleeContext.Real
←F.[[Realm]]
calleeContext.ScriptOrModule
←F.[[ScriptOrModule]]
.
- Let
localEnv
beNewFunctionEnvironment(F, newTarget = undefined)
.localEnv.outerEnvironment
isF.[[Environment]]
(the declarative environment where the function declaration is instantiated in)calleeContext.LexicalEnvironment
←localEnv
.calleeContext.VariableEnvironment
←localEnv
.
- If
callerContext
is not already suspended, suspend callerContext. - Push
calleeContext
onto the execution context stack;calleeContext
is now the running execution context.
- Perform
OrdinaryCallBindThis(F, calleeContext, thisArgument)
.- Let
thisMode
beF.[[ThisMode]].
- If
thisMode
islexical
, return NormalCompletion(undefined). - If
thisMode
is strict, letthisValue
bethisArgument
. calleeContext.LexicalEnvironment.envRec.BindThisValue(thisValue)
.
- Let
- Let result be
OrdinaryCallEvaluateBody(F, argumentsList)
.FunctionDeclarationInstantiation(F, argumentsList)
calleeContext
= the running execution contextenv
=calleeContext
.LexicalEnvironmentenvRec
= env.EnvironmentRecordhasParameterExpressions
=ContainsExpression
offunc.[[FormalParameters]]
hasParameterExpressions
isfalse
:varEnv
=lexEnv
=env
NOTE: Only a single lexical environment is needed for the parameters and top-level vars.
hasParameterExpressions
istrue
:varEnv
=lexEnv
=NewDeclarativeEnvironment(env)
NOTE: A separate Environment Record is needed to ensure that closures created by expressions in the formal parameter list do not have visibility of declarations in the function body.
- Declarations
- vars:
varEnv.CreateMutableBinding
forVarDeclaredNames
offunc.[[ECMAScriptCode]]
and initialize them withundefined
- lexical:
lexEnv.CreateImmutableBinding
orlexEnv.CreateMutableBinding
forLexicallyScopedDeclarations
offunc.[[ECMAScriptCode]]
(not initialized) - functions: Create function objects for function declarations of
VarScopedDeclarations
offunc.[[ECMAScriptCode]]
withLexicalEnvironment
as function object’s[[Environment]]
. AndvarEnv.SetMutableBinding
with these function objects.
- vars:
- Remove
calleeContext
from the execution context stack and restorecallerContext
as the running execution context. - If result.[[Type]] is return, return
NormalCompletion(result.[[Value]]). ReturnIfAbrupt(result)
. - Return
NormalCompletion(undefined).
We can see the same behavior of “hoisting” in a global context:
- Lexical declarations are only instantiated here but not initialized.
- Function declarations are instantiated and initialized with the function object as its value.
- Var declarations are instantiated and initialized with
undefined
as its value.
However, the LexicalEnvironment
and VariableEnvironment
here are calleeContext
’s LexicalEnvironment
, which are different from the global ones. This is related to the concept of “scope,” which has not been covered yet.
Recap
Here is the outline of the steps to prepare a new execution context for a function call:
-
Throw a “TypeError exception” if the function is a class constructor.
-
Create a new ECMAScript code execution context for Callee.
-
calleeContext.LexicalEnvironment.envRec.BindThisValue(thisValue)
-
Initiate the declarations in the function:
-
Parameters:
func.[[FormalParameters]]
- bound to
calleeContext.LexicalEnvironment.envRec
- bound to
-
When a function has parameter expressions, other variables are bound to the new inner Declarative Environment’s record to ensure that closures created in the parameter list do not have visibility of declarations in the function body. Otherwise bound to
calleeContext.LexicalEnvironment.envRec
// Example of a function with parameter expressionsfunction outer(param = () => innerVar) {// Function bodylet innerVar = "I am inside the function body";return param();}// Call the function and see what happenstry {console.log(outer()); // This will throw a ReferenceError} catch (e) {console.log(e.message); // innerVar is not defined} -
hasParameterExpressions
=ContainsExpression
offunc.[[FormalParameters]]
hasParameterExpressions
isfalse
:varEnv
=lexEnv
=env
NOTE: Only a single lexical environment is needed for the parameters and top-level vars.
hasParameterExpressions
istrue
:varEnv
=lexEnv
=NewDeclarativeEnvironment(env)
NOTE: A separate Environment Record is needed to ensure that closures created by expressions in the formal parameter list do not have visibility of declarations in the function body.
-
Declarations
- vars:
varEnv.CreateMutableBinding
forVarDeclaredNames
offunc.[[ECMAScriptCode]]
and initialize them withundefined
- lexical:
lexEnv.CreateImmutableBinding
orlexEnv.CreateMutableBinding
forLexicallyScopedDeclarations
offunc.[[ECMAScriptCode]]
(not initialized) - functions: Create function objects for function declarations of
VarScopedDeclarations
offunc.[[ECMAScriptCode]]
withLexicalEnvironment
as function object’s[[Environment]]
. AndvarEnv.SetMutableBinding
with these function objects.
- vars:
-
-
Evaluate the statements in the function body
-
Pop the callee execution context from stack and resume the caller execution context as the running execution context