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
localEnvbeNewFunctionEnvironment(F, newTarget = undefined).localEnv.outerEnvironmentisF.[[Environment]](the declarative environment where the function declaration is instantiated in)calleeContext.LexicalEnvironment←localEnv.calleeContext.VariableEnvironment←localEnv.
- If
callerContextis not already suspended, suspend callerContext. - Push
calleeContextonto the execution context stack;calleeContextis now the running execution context.
- Perform
OrdinaryCallBindThis(F, calleeContext, thisArgument).- Let
thisModebeF.[[ThisMode]]. - If
thisModeislexical, return NormalCompletion(undefined). - If
thisModeis strict, letthisValuebethisArgument. calleeContext.LexicalEnvironment.envRec.BindThisValue(thisValue).
- Let
- Let result be
OrdinaryCallEvaluateBody(F, argumentsList).FunctionDeclarationInstantiation(F, argumentsList)calleeContext= the running execution contextenv=calleeContext.LexicalEnvironmentenvRec= env.EnvironmentRecordhasParameterExpressions=ContainsExpressionoffunc.[[FormalParameters]]hasParameterExpressionsisfalse:varEnv=lexEnv=envNOTE: Only a single lexical environment is needed for the parameters and top-level vars.
hasParameterExpressionsistrue: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.CreateMutableBindingforVarDeclaredNamesoffunc.[[ECMAScriptCode]]and initialize them withundefined - lexical:
lexEnv.CreateImmutableBindingorlexEnv.CreateMutableBindingforLexicallyScopedDeclarationsoffunc.[[ECMAScriptCode]](not initialized) - functions: Create function objects for function declarations of
VarScopedDeclarationsoffunc.[[ECMAScriptCode]]withLexicalEnvironmentas function object’s[[Environment]]. AndvarEnv.SetMutableBindingwith these function objects.
- vars:
- Remove
calleeContextfrom the execution context stack and restorecallerContextas 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
undefinedas 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=ContainsExpressionoffunc.[[FormalParameters]]hasParameterExpressionsisfalse:varEnv=lexEnv=envNOTE: Only a single lexical environment is needed for the parameters and top-level vars.
hasParameterExpressionsistrue: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.CreateMutableBindingforVarDeclaredNamesoffunc.[[ECMAScriptCode]]and initialize them withundefined - lexical:
lexEnv.CreateImmutableBindingorlexEnv.CreateMutableBindingforLexicallyScopedDeclarationsoffunc.[[ECMAScriptCode]](not initialized) - functions: Create function objects for function declarations of
VarScopedDeclarationsoffunc.[[ECMAScriptCode]]withLexicalEnvironmentas function object’s[[Environment]]. AndvarEnv.SetMutableBindingwith 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