function foo(a,b){
return a + b;
}
foo(1,2);
Are function parameters hoisted?
Does the variableEnvirnoment at the creation phase of the function execution context looks something like that :
VE = {
{ 0 : undefined , 1: undefined, length: 2 },
{a : undefined, b: undefined},
outer: refToGlobalLE
}
Yes, parameters are hoisted.
When a function is called, each declaration in the function body (var, let, const, and function declarations) is instantiated in the environment record for that execution context. Each formal parameter is also added to the environment record in the same way ([9.2.10 21.c.i and 9.2.10 28.e.i.2]). The full process is described in section 9.2.10 of the spec.
Formal parameters and function declarations are both initialised (note: not "instantiated") during the abstract operation called FunctionDeclarationInstantiation. All other bindings are initialized during evaluation of the function body.
This means that formal parameter bindings are added into the same logical location, in the same way, as those of function body declarations ie. var, let, const, and function declarations (all of which are hoisted, by the way). And it means that formal parameter bindings are initialized in the same way as function declarations (ie. that their corresponding value is set at the top of the function).
Hoisting is a function of the time and place an identifier binding is instantiated. Formal parameter bindings are instantiated (and initialized) in the same way as function declarations. We know function declarations are hoisted, therefore parameter bindings are hoisted. QED.
Prior to ES2015 this hoisting was, as far as I know, invisible, because function parameters are already at the very top of a function. However, parameter default value initializer syntax was added in ES2015, making the hoisting visible in userland.
If formal parameters were not hoisted then the following code would not throw "Uncaught ReferenceError: Cannot access 'x' before initialization" because the default parameter value for z would refer to the outer x:
var x = 'global'
(function(y, z = x, x) {}()) // "Uncaught ReferenceError: Cannot access 'x' before initialization"
Your comment says that 'assuming that parameters are hoisted, x would [be initialized to] "undefined", and as a result the error would not occur as x is already initialized'. Your implication is that hoisting could also be used to explain the absence of an error. This is true, but it does not therefore follow that if an error is observed, hoisting cannot be occurring. Indeed, we can see hoisting is occurring because, as specified in the error message, x in z = x is taken to refer to formal parameter x which is declared later in the program text. The only way this is possible is for hoisting to have occurred.
No, The functions parameters are not hoisted in JavaScript.
function foo(a,b){
return a + b;
}
foo(1,2);
To explain you in brief about the whole process, I would like to explain about how the function itself gets called.
So since you are speaking about the variable environment(which in this case would be the global execution context), the JavaScript thread of execution which basically executes the code line by line would store the function definition code as a value to the foo variable in the memory and it does not executes it yet (it just saves the definition as is). It moves to the next execution line which apparently makes a function call(using parenthesis) to that foo function definition.
Now, when that function gets called foo(1,2), first a new execution context gets created inside the global execution for that foo function. You can imagine this as a box abstracted inside. The arguments 1 and 2 gets mapped with the 'a' and 'b' parameters of the function as variables inside the foo execution context and not the global execution context. Then, the function just returns the value of a+b to the global execution context through the call stack.
Okay, so to check this, if you would have just called the function and then after that defined it like below
foo(1,2);
function foo(a,b){
return a + b;
}
You would probably get an error since the foo function definition is not present in the global execution context and would basically result into a REFERENCE ERROR.
Now, talking about the parameters itself, whether they get hoisted or not. Then No! Those are itself created when the function gets called and a new execution context is created. These variables are block scoped to the function and are not visible to the outer/global execution context. So as and when the function gets returned to the callee, the variable inside of it are just garbage collected.
Hope that answers your question.
Related
Sometimes I read "invocation context" sometimes "execution context". I would like to know if we are talking about the same concept.
I must say that in ECMAScript6 specification I don't find any reference to the "invocation context".
The two terms are closely related but not the same thing.
In a nutshell they define scope vs. context. Scope is about the environment that code runs in (kind of like a room - - it's about where the code is) and context is about an actual object that caused some code to be executed (like who was responsible for putting you in that room).
An "execution context" refers to the "scope chain" that is in
effect when some code is running. A scope chain is a list of
memory locations that should be checked (in a particular order) for
identifiers (variable, constant and function names) to be resolved to
a value. Since JavaScript is executed in a single-threaded
environment, only one task can be executed at a time. The currently
executing code (and its associated scope) define the execution
context.
A simple example can be shown like this:
// This area is in the Global execution context (scope) because the code is
// not wrapped in a function or any other kind of code block.
var x = "Global";
// "Global" is the result because the JavaScript engine will always look
// in the current scope for a declaration for the identifier in question.
// It will find a declaration for "x" right here in the Global scope, so
// that's the value it will use.
console.log(x);
var y = "Also Global";
function parent(){
// This area is in the "parent" execution context (scope)
var x = "parent";
// "parent" is the result (not "Global") because when this function is
// executing, its scope is the most accessible. The JavaScript engine
// looks here first to find out what "x" is. This is known as variable
// "hiding" because the x in the parent scope hides the x in the Global scope.
console.log(x);
function child() {
// This area is in the "child" execution context (scope)
var x = "child";
// "child" is the result (not "Global" or "parent") because when this
// function is executing, its scope is the most accessible. The
// JavaScript engine looks here first to find out what "x" is. This
// x now hides the x in parent, which is hiding the x in Global.
console.log(x);
// "Also Global" is the result here. First the current execution
// context (scope) is checked for a "y" variable. There isn't one,
// so the next scope in the scope chain (function parent) is checked.
// There is no "y" declared there either. So, again, the next highest
// scope in the chain (Global) is checked and that is where "y" is
// found, so the value of that "y" is used.
console.log(y);
// Here, we will get "undefined". All the scopes in the chain will
// be checked and if we go all the way up to Global and still don't
// find a declaration for "z", there is no other scope to look in.
console.log(z);
}
child();
}
parent();
The three execution contexts (scopes) shown above can be entered into in various ways. Those various ways give rise to the "invocation context".
An "invocation context" can also more accurately be called an
"invocation context object" which refers to the object that was
used to invoke some code. This may seem like the same thing as the
"execution context", but the "invocation context" refers to the
object that leads to code executing and that executing code is doing so
within its own execution context (scope).
The biggest differentiating point between these two terms is understanding that the invocation context leads to the binding of the keyword this to an object for the duration of the execution context. this binding is volatile in JavaScript and the object that this binds to is subject to change as you enter into a new execution context. For all intents and purposes, this is the object that invoked some code, or this is the "invocation context".
I've written another answer that goes into more detail about how to determine what this will bind to and you can see that here.
See the following snippet for an explanation of invocation context. It illustrates one execution context (function foo), but two invocation contexts (the button and Global).
function foo() {
// When this function is "invoked" via the button click
// the invocation context is the button and the console will
// log this as: [object HTMLButtonElement]
// But, when the function is invoked from a direct call to foo
// from the Global execution context, this will be bound
// to the window object. So, in that case we'll get: [object Window]
console.log("The 'this' object (invocation context object) is: " + this);
}
// Call foo from the Global execution context.
foo();
var btn = document.getElementById("btnTest");
// When the button is clicked, execute foo
btn.addEventListener("click", foo);
<button id="btnTest">Click Me</button>
"execution context" is the official term (i.e. used by the specification) for a stack frame. As the wording says, it provides the context for the (current) execution of code, which basically consists of internal evaluation state, the function (code) that is currently evaluated, and the active variable scope. Being part of a stack, it knows where to return to when the function execution ends.
"invocation context" probably refers to the context object on which a method is invoked, i.e. the obj in obj.method(). It will become the value for this in that function. The spec doesn't use this term anywhere, what you'll find are thisArgument, thisValue and receiver. It's referred to as ThisBinding in the environment record that is part of the lexical environment (scope) that is contained in every execution context.
I can't figure out why an unexecuted closure can capture the outer variables.
I do read about some articles about execute context, lexical environment, memory management, but none of these can solve my question:
function foo() {
var a = 1;
return function() {
console.log(a);
}
}
var f = foo() // line 7
// HERE variable a was been captured
f = undefined // line 10
// HEAE variable a was been released
When the engine execute to line7, the foo execution context was created above global execution context, but after line7 the closure was never been execute, so the closure execution context was never been created, so was the lexical environment. foo execution context was been popped up, the variable a will be released.
I can't find what's wrong with my point.
So why and when the variable in closure be captured?
function foo() {
var a = 1;
return function() {
console.log(a);
}
}
Function inside function are known as closure.
When one function has another function inside it, and say top level function has some data like 'a' in this case, then all the inner functions will get access to that data, this will happen only if inner function has some reference about those variables.
Let's say if you have 'b' variable beside 'a' and you are not using it anywhere in inner functions this will be simply ignored by javascript. 'Closure' will not hold this value like 'a'.
This is actually where closure gives devs power.Check out below example -
const test = (num1) => (num2) => console.log(num1*num2);//closure is in action here
var s = test(100)
s(2); // this will give output of 200
Hope this helps.
Thanks.
Here is a discussion on bugs.chromium that is related to your question. As per this , even when the else block never executed , reference to an outer object used in function returned by else block is created and stored in heap. It is never garbage collected. Refer to below discussion :-
https://bugs.chromium.org/p/chromium/issues/detail?id=315190
The key point is lexical scope:
Lexical scope is the scope model used by the JavaScript language, which differs to some other languages which use dynamic scope. Lexical scope is the scope defined at lexing time.
Considering this:
var a = 1
console.log(a)
console.log(b)
var b = 2
console.log(c)
You can get the result:
1
undefinded
ReferenceError: c is not defined
So, you can see how JavaScript handling the variables: all variable are defined at lexing time and assignment at runtime. This is what they called hoisting.
Back to the question: closures capture variables at lexing time in which js engine read your code and defined variable and bind them.
Read more about compilation at: https://v8.dev/blog/background-compilation
The ECMAScript specification goes into detail about what happens when control enters the execution context of a function within a function.
function foo() {
function bar() {
}
bar(); // Control will be given to the bar function. Details specified by spec
}
There also is an explanation of what happens when control enters global code.
<script>
// Entering global code! Details specified by spec
</script>
However, there is nothing specifying what happens when entering control for a function defined in the global code.
<script>
function foo() {
}
foo(); // Calling a function defined in the global environment...not specified by spec
</script>
Edit: The reason this is important to me is because I'm curious what the internal [[Scope]] property of the function called by the global code will be. I assume it will be the lexical environment of the global execution context, but there's nothing that specifies this in the specification.
I think you misinterpreted that sentence (from §10.4.3, Entering Function Code):
The following steps are performed when control enters the execution context for function code contained in function object F, a caller provided thisArg, and a caller provided argumentsList […]
It does not mean that the function which is entered must be contained in F, but that the code that is entered is contained in the function F (which you are invoking).
The [[Call]] method which is used when calling a function does not distinguish between global/local declared or invoked functions.
Functions declaraed in global code are instantiated during Declaration Binding Instantiation of the global code in step 2 of 10.4.1.
This is done via step 5.c of 10.5 which actually creates each such function object via the first algorithm in section 13. Note that this sets the [[Scope]] of the function to the VariableEnvironment of the current execution context. The current execution context was set by step 1 of 10.4.1 (via 10.4.1.1) to the Global Environment.
Consider the following snippet of JavaScript:
function outer() {
var x = 10;
function inner() {
var y = 20;
}
}
Obviously, the variable y is not available in the context of outer. Following through the process in the ES5 spec tells us that this is what happens when we enter the outer execution context:
We enter a new execution context (10.4)
Since it's a function, we follow the steps outlined in Entering Function Code (10.4.3)
Let code be the value of F’s [[Code]] internal property.
The [[Code]] internal property of a function is described in Creating Function Objects (13.2). It refers to the FunctionBody as specified by the grammar (the entire contents of the function):
function Identifier ( FormalParameterListopt ) { FunctionBody }
At this point we enter the Declaration Binding Instantiation (10.5) section and do the following:
Create bindings for any arguments in the formal parameter list of the function
For each function declaration in code:
Create a binding in the current scope between function identifier and a Function object (created as specified in Function Definition (13))
There is nothing to say "remove the function we've just processed from code"
Create a binding for the arguments object
For each variable declaration in code:
Create a binding in the current scope for the variable identifier
My question is why, at this point, are bindings not created for variable declarations from inner functions? It seems like code should still contain the entire source text of the outer function (which would include the source text of the inner function). I'm looking for something in the spec which explains the behaviour.
Updated, to be a bit clearer: I am asking about what happens when we enter the context of the outer function. The inner function is never called, and I don't care about what happens when we return from the outer function. I am purely interested in the process, as defined by the spec, of creating bindings for variable declarations when entering a new execution context.
You have a error in your thinking.
Javascript has function-scope. Thus you have a new execution context for each function, thats true. However, after you return from a function this function context expires. That is the reason why you can't access the variables from the inner function after it has returned. You still are in the execution-context of the outer-function but you can't access the execution-context of the inner function anymore.
Citing the spec:
Every return exits an execution context. A thrown exception may also exit one or more execution contexts.
EDIT: To clarify this further: The body of a function declaration is NOT being processed (see 10.5.5.d), only the function identifier and the arguments are passed to the variable environment.
It's right there in the definition of the term code (§10.1):
Function code is source text that is parsed as part of a FunctionBody. The function code of a particular FunctionBody does not include any source text that is parsed as part of a nested FunctionBody.
In a comment on another thread I started, someone said this:
#adlwalrus yes. try this: var foo = function bar(){}; console.log(foo); But be aware that bar is only function name (what does it mean I'm not sure exactly myself) and not a reference to it, so you can't call it by doing bar(). And assigning (even named function) is not the same as declaring a function. Hoisting (bumping to top of the scope) only works for declarations, assignment will stay in place. – valentinas 6 hours ago
What purpose does a function name serve if you can't call it with bar()?
For the function to call itself.
var x = function y(val){
if (val){
console.log(val);
y(val-1);
}
};
x(5);
> 3
> 2
> 1
y(3);
> ReferenceError: y is not defined
You're referring to a named function expression. The spec requires that the name of such functions only be available within the scope of the new function. Spec quote:
The Identifier in a FunctionExpression can be referenced from inside
the FunctionExpression's FunctionBody to allow the function to call
itself recursively. However, unlike in a FunctionDeclaration, the
Identifier in a FunctionExpression cannot be referenced from and does
not affect the scope enclosing the FunctionExpression.
On the other hand, the result of that expression is a reference to the new function, which can be saved and referenced anywhere.
Lots of details here:
http://kangax.github.com/nfe/#named-expr
I'd read the whole thing.
As for benefits, another is that it makes them easier to identify in a debugger.
There are two ways to create a function in JavaScript, a "function declaration" and a "function expression." I believe it was Doug Crockford who explained it best when he pointed out that unless "function" is the very first set of characters on a given line, you're performing a function expression (not a declaration).
Function declarations are finicky creatures. You'll recognize them when you see them. They look like this:
function foo() { /* ... */ }
They're always given a name (it's requited) and the name is locally scoped to the lexical context under which the function is declared. So if you perform a function declaration in the global context, then the function can be referenced via it's name globally. If you do it within a function, the function's name can be referenced only within that function and any functions declared within that function.
I think the most important aspect of this method of declaring a function (one that is rarely commented on) is that the function initialization gets hoisted to the top of the current lexical context. Therefore, you should never, ever use a function declaration within a conditional, such as this:
//DON'T DO THIS!
if (x) {
function foo() { return 1; }
} else {
function foo() { return 2; }
}
foo(); //will always be 2, regardless of the value of x.
A function expression is slightly different. Often, they're directly assigned to a variable, like so:
var foo = function() { /* ... */ };
This is nearly identical to the function declaration above except that the initialization is not hoisted. So you can do the following:
var foo;
if (x) {
foo = function() { return 1; };
} else {
foo = function() { return 2; };
}
foo(); //will be 1 or 2, depending on the truthy-ness of x.
So, back to the original question. Function expressions can also have a name, though it's not required and it's not scoped to the context in which the function is declared (as with function declarations). Instead, it gets scoped to the function's own lexical context. This is very useful in some cases. My personal favorite is this pattern:
(function foo() {
//Do something.
setTimeout(foo, 1000);
}());
foo; //undefined
Because of the parenthesis before the word "function", this is a function expression and the name is scoped internally only. But that's okay, because we only need to call it internally (via setTimeout()). The result is that the function will execute once immediately, then will re-execute every second or so after it's finishes execution. This is safer than using setInterval() because it will wait until it's done executing before rescheduling itself, preventing overlaps that could cause missed executions and/or "domination" of the JavaScript thread.
Essentially, the use of a named function expression is limited, but when you need it, it's very powerful.