I came through this fun quiz on the internet.
console.log((function(x, f = (() => x)){
var x;
var y = x;
x = 2;
return [x, y, f()]
})(1))
and the choices were:
[2,1,1]
[2, undefined, 1]
[2, 1, 2]
[2, undefined, 2]
I picked solution 2 TBH, basing that on that x has been redefined, y was declared and defined with no value, and that f has a different scope hence getting the global x memory spot than function x memory spot.
However, I tried it in jsbin.com
and I found it was solution 1, while I was not sure why that happened I messed with the function body and I removed var x from the function body, I found that the response changed to #3 which makes sense as x value changed and hence it showed x and f as 2 and y as 1 which was declared globally.
but still I can't get why it shows 1 instead of undefined.
but still I can't get why it shows 1 instead of undefined.
It's not just you. This is a deep, dark part of the specification. :-)
The key here is that there are two xs. Yes, really. There's the parameter x, and there's the variable x.
A parameter list containing expressions (like f's default value) has its own scope separate from the function body's scope. But prior to parameter lists possibly having expressions, having var x within a function with an x parameter had no effect (x was still the parameter, with the parameter's value). So to preserve that, when there's a parameter list with expressions in it, a separate variable is created and the value of the parameter is copied to the variable at the beginning of the function body. Which is the reason for this seemingly-odd (no, not just seemingly) odd behavior. (If you're the kind who likes to dive into the spec, this copying is Step 28 of FunctionDeclarationInstantiation.)
Since f's default value, () => x, is created within the parameter list scope, it refers to the parameter x, not the var.
So the first solution, [2, 1, 1] is correct, because:
2 was assigned to the var x in the function body. So at the end of the function, the var x is 2.
1 was assigned to y from the var x before x got the value 2, so at the end of the function, y is 1.
The parameter x's value has never changed, so f() results in 1 at the end of the function
It's as though the code were written like this instead (I've removed unnecessary parens and added missing semicolons):
console.log(function(param_x, f = () => param_x) {
var var_x = param_x;
var y = var_x;
var_x = 2;
return [var_x, y, f()];
}(1));
...I removed var x from the function body, I found that the response changed to #3...
#3 is [2, 1, 2]. That's correct, because when you remove the var x from the function, there's only one x, the parameter (inherited by the function body from the parmeter list). So assigning 2 to x changes the parameter's value, which f returns.
Taking the earier example with param_x and var_x, here's what it looks like if you remove the var x; from it:
console.log(function(param_x, f = () => param_x) {
var y = param_x;
param_x = 2;
return [param_x, y, f()];
}(1));
Here's an annotated description of the original code (with the extraneous parentheses removed and missing semicolons added):
// /---- the parameter "x"
// v vvvvvvvvvvv--- the parameter "f" with a default value
console.log(function(x, f = () => x) {
var x; // <=== the *variable* x, which gets its initial value from the
// parameter x
var y = x; // <=== sets y to 1 (x's current value)
x = 2; // <=== changes the *variable* x's value to 2
// +---------- 2, because this is the *variable* x
// | +------- 1, because this is the variable y
// | | +--- 1, because f is () => x, but that x is the *parameter* x,
// | | | whose value is still 1
// v v vvv
return [x, y, f()];
}(1));
Final note regarding your title:
declaring a variable twice in IIFE
The variable is only declared once. The other thing is a parameter, not a variable. The distinction is rarely important...this being one of those rare times. :-)
The tricky part of that code is that the => function is created as part of a default parameter value expression. In parameter default value expressions, the scope includes the parameters declared to the left, which in this case includes the parameter x. Thus for that reason the x in the => function is in fact the first parameter.
The function is called with just one parameter, 1, so when the => function is called that's what it returns, giving [2, 1, 1].
The var x declaration, as Mr Crowder points out, has the (somewhat weird, at least to me) effect of making a new x in the function scope, into which is copied the value of the parameter x. Without it, there's only the one (the parameter).
Related
Tried to figure out the working logic behind the results. I couldn't.
Could someone please help explain how Javascript works the output of each of the 4 situations and the reason behind it?
I think the 1st and 2nd are similar situations.
Thanks
var num = (1, 2, 3); // num = 3 (last number returned, No error generated)
console.log(num)
y = (1, 2, 3); // y = 3 (last number returned, No error generated)
console.log(y)
z = 1, 2, 3; // z = 1 (first number returned, No error generated)
console.log(z)
// Uncomment the following to run: (gives an error)
//var m = 1, 2, 3; // Error: Unexpected number
Your first three snippets are all basically the same. They demonstrate usage of the Comma Operator.
The Comma Operator a, b first evaluates a and throws away the result, then evaluates b and returns the result. It is left-associative, so a, b, c is the same as (a, b), c.
Observe:
const foo = () => { console.log("foo"); return 23; };
const bar = () => { console.log("bar"); return 42; };
const qux = () => { console.log("qux"); return 99; };
console.log((foo(), bar()));
// foo
// bar
// 42
console.log((foo(), bar(), qux()));
// foo
// bar
// qux
// 99
Note that I could have written the three functions like this, but of course it doesn't make much sense to use the Comma Operator while trying to explain it:
const foo = () => (console.log("foo"), 23);
const bar = () => (console.log("bar"), 42);
const qux = () => (console.log("qux"), 99);
console.log((foo(), bar(), qux()));
// foo
// bar
// qux
// 99
In your first snippet, you have a Variable Declaration statement, which declares a variable, evaluates the initializer, and binds the value of the initializer to the variable. The initializer in this case is 1, 2, 3, which is equivalent to (1, 2), 3. So, it will evaluate 1, throw away the result, evaluate 2 return the result 2 as the result of evaluating the sub-expression 1, 2 but then immediately throw it away, evaluate 3 and return the result 3 as the result of the evaluation.
Therefore, it binds the value 3 to the variable num.
The second snippet is exactly the same, except it is an Assignment Expression, not a Variable Declaration, i.e. in Strict Mode, it will assign to a variable named y that was previously declared with let or var and error out with a static error otherwise. In Loose Mode, it will assign to a variable named y that was previously declared with let or var, or to a property named y of the global implicit object otherwise.
The evaluation of the right-hand-side of the assignment is exactly the same as above.
Snippet #3 uses again the Comma Operator. The Comma Operator has higher precedence than the Assignment Operator (the way ECMAScript is specified, it does not contain a simple operator precedence table, rather, the precedence is implicit in the grammar, so you'll either have to read the entire grammar or believe me), so your snippet #3 is equivalent to
((z = 1), 2), 3;
Again, it works just the same as snippets #1 and #2: first, the expression z = 1 is evaluated (which has the side-effect of assigning 1 to z) and the result is thrown away, then the expression 2 is evaluated and the result is thrown away, then the expression 3 is evaluated, and the result is returned.
So, the result of the entire expression is 3, but since that result is not assigned to anything, not returned, not printed, it is simply thrown away. All that is left is the side-effect that happened while evaluating the first expression, namely assigning 1 to z.
The last expression is different, because it does not demonstrate the use of the Comma Operator. It is, again, a Variable Declaration, just like snippet #1. A Variable Declaration basically is a list of comma-separated identifiers with optional initializers. The reason you get an error here, is that 2 is not a valid identifier for a variable.
Observe:
var n = 1, o, p;
// Declares variables `n`, `o`, `p`, initializes `n` to `1`.
console.log(n, o, p);
// 1 undefined undefined
Now, for comparison, your snippet #4 is ecactly the same:
var m = 1, 2, 3;
// Declares variables `m`, `2`, `3`, but `2` and `3` are not legal names.
The major difference between this snippet #4 and the other three is that the comma means a completely different thing here. In snippets #1 to #3, it is the Comma Operator, whereas in snippet #4, it is part of the Variable Declaration syntax.
There are other places where the comma can mean something different, for example parameter lists (function foo(a, b, c) or (a, b, c) => bla) or argument lists (foo(1, 2, 3)). In both cases, the comma is not the Comma Operator but part of the parameter list or argument list syntax to separate the parameters or arguments.
Observe:
console.log(1, 2, 3);
// 1 2 3
console.log((1, 2, 3));
// 3
In var num = (1, 2, 3); num is being set to the value of the final expression in the set. It's like a shorthand to evaluate multiple expressions and return the last value.
y = (1, 2, 3); is the same thing when you're at top level scope.
z = 1, 2, 3; is three expressions, z = 1, 2, and 3. z is set to 1, but if you logged the output of that line, you'd get 3.
var m = 1, 2, 3; is a syntax error because in this situation, the interpreter is expecting a single var pattern like var m = 1, n = 2, o = 3.
console.log((function(x, f = () => x) {
var x;
var y = x;
x = 2;
return [x, y, f()];
})(1));
This is the code snippet. The output here is (3) [2, 1, 1]
How is the third output 1 and not 2?
Also, if I run this code on Scratch JS, it prints (3) [2, 1, 2]
Why is the output different here?
I tried passing a second argument in the IIFE function as in:
console.log((function(x, f = (z) => z) {
var x;
var y = x;
x = 2;
return [x, y, f(z)];
})(1, 2));
but this throws an error
Output is (3) [2, 1, 1] in chrome console, but (3) [2, 1, 2] in Scratch JS
How is the third output 1 and not 2? [...] Why is the output different here?
If the code is run as written, then the default parameter is evaluated before entering the function. So the x in f = () => x is referring to the first parameter of the function, not the var x defined inside of it. And since 1 was passed in to the IIFE, 1 is the value used.
Why is the output different here?
It appears that scratchJS is transpiling to an older version of javascript. This turns the code into something like this:
console.log(function (x) {
var f = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : function () {
return x;
};
var x;
var y = x;
x = 2;
return [x, y, f()];
}(1));
Now the default value is handled by code inside the function body. Because of hoisting, var x is the variable that's referred to when doing return x, not the x in the argument list. By the time f is called, that local variable has been set to 2, so 2 is logged.
In the first case, () => x is returning 1 because you didn't pass a function as 2nd argument, that means the arrow function is instantiated outside of your function and keeps its own context. the x inside the function is confined to a different context.
This is a classic issue with shadowed name variables, same name but different nested context.
In the second example you are passing the value 2 as a function in your arguments, which is why it's throwing. You can't execute 2().
I'd like to understand the debugging content during the execution of the following code:
var z = 2;
var SQUAREPLUSOTHER = x => y => ((x*x) + y + z);
var x = 3;
console.log("SQUAREPLUSOTHER", SQUAREPLUSOTHER);
var squareoftwoplusother = (SQUAREPLUSOTHER)(x);
x = 4;
z = 4;
var result = squareoftwoplusother(5);
console.log("result", result);
Now....
At the call of:
console.log("SQUAREPLUSOTHER", SQUAREPLUSOTHER);
the debug shows clearly:
squareoftwoplusother: undefined
SQUAREPLUSOTHER: x=>...
x: 3
z: 2
at the following call of:
var SQUAREPLUSOTHER = x => y => ((x*x) + y + z);
the debug shows:
Local:
x: 3
Closure
z: 2
at the following call of:
x = 4;
z = 4;
the debug shows:
squareoftwoplusother: y=>...
SQUAREPLUSOTHER: x=>...
x: 4
z: 4
at the following call of:
var result = squareoftwoplusother(5);
the debug shows:
result: 18
squareoftwoplusother: y=>...
SQUAREPLUSOTHER: x=>...
Local:
x: 5
Closure
x: 3
Closure
z: 4
and at the final calls the debug shows:
result: 18
squareoftwoplusother: y=>...
SQUAREPLUSOTHER: x=>...
x: 4
z: 4
now the questions:
How many "Closures"? They belongs to? (i.e. how to explain...)
Local:
x: 5
Closure
x: 3
Closure
z: 4
How the scope of the variables is managed in javascript?
How to have a "definitive" idea in terms of "context" or "whatever it be" about the mess of closure :-)?
thanks in advance
Ed
Every time a function is called, a local scope is created for that function. If there is still a reference to that local scope after the function returns, then a closure is created. Once the reference is gone, the closure is also garbage collected.
If you add breakpoints to the first line of every function, you will see that scopes are created that may later become closures.
A great explanation is given here http://dmitrysoshnikov.com/ecmascript/javascript-the-core/#activation-object
I'll add some braces to your code to make that easier and I'll add self calling function to add a level of closures.
(() => {
var z = 2;
var SQUAREPLUSOTHER = (x) => {
debugger;
return (y) => {
debugger;
return (x * x) + y + z;
}
};
var x = 3;
console.log("SQUAREPLUSOTHER", SQUAREPLUSOTHER);
var squareoftwoplusother = (SQUAREPLUSOTHER)(x);
x = 4;
z = 4;
var result = squareoftwoplusother(5);
console.log("result", result);
debugger
})()
The screenshots below are from Chrome DevTools which minimizes the closures, that is, it only maintains references to what is needed. Back in the days, the entire closure was always available and you could prove it because it showed up in the closures pane of debuggers. See Javascript closures performance for further details
Scope
There are just three different scopes in your code. One is the global scope all variables you declare in and the other two are here:
var SQUAREPLUSOTHER = x => y => ((x*x) + y + z);
There are two function scopes, one containing the variable x and the other function scope containing the variable y. It might be easier to see if you replace the arrow function with functions:
// global scope
function SQUAREPLUSOTHER(x) {
// function scope containing x
return function(y) {
// function scope containing y
return (x*x) + y + z;
};
}
How scoping works is actually quite simple:
Every { starts a new scope and its counter } closes it, variables declared inside it with let and const (or var but thats slightly more complicated) are part of the scope. If it is the scope of a function, the parameters (e.g. x and y) are part of that scope.
From the code, you can access the all variables that are in the current scope or in its parent scope, if there are multiple with the same name you get the most inner one. Therefore x inside of the SQUAREPLUSOTHER function refers to the variable of the SQUAREPLUSOTHER scope, while for code outside of it x is the global variable and the functions variable can't be accessed.
The Scope of a variable does not change at runtime, you can always directly see which scope a variable belongs tp by looking at the surrounding { .. }.
Now different variables in different scopes have to hold values at runtime, thats where we get to:
the environment record
When you call a function, the JavaScript engine creates a new "EnvironmentRecord" (which is like an internal object) that contains all the variables of the function you call, e.g. in this case:
function test(a) {
let b;
}
Then if you call that function (test(1)) a new environment record gets created that contains a and b. Now the code inside the function gets run, and every variable is looked up in it. If there are two functions nested into another, calling the inner function will create an environment record that holds a reference to the outer one:
function test(a) {
function test2(b) {
}
test2(5);
}
Now calling test(1) will create an record where a is 1. If the engine then executes the second call (test2(5)) it creates another record containing b being 5, and that holds a reference to the record containing a. Now if you use a inside test2, the engine will look it up in the current environment record, won't find it, and then look it up in the parent where it finds a being 1.
closure
Usually those records get deleted when the execution reaches the }, however if there is another record that got the current record as a parent, it won't get deleted. It will exist until all child records got deleted, then it will also remove the parent. This behaviour (variables live longer because they can be accessed from an inner functions body) is called a closure.
It is said in w3cschool that "If a function changes an argument's value, it does not change the parameter's original value."
However, i don't quite understand that with the following example:
function bar(a){
arguments[0] = 10;
console.log("a",a);//10
return a;
}
function foo(cc){
cc = 10;
return arguments[0];
}
console.log(bar(333));//10
console.log(foo(333));//10
I have tested them in both chrome and firefox.
From my understanding , if argument value changes can not lead to parameter value change, why 'bar' fail to return 333?
Given a function:
function bar(a) {
arguments[0] = 10;
return a;
}
Calling it like bar(50) will return 10, because the value inside bar's scope was replaced by 10.
What "If a function changes an argument's value, it does not change the parameter's original value." means is that doing:
var x = 90;
var y = bar(x);
console.log(y);
console.log(x);
// y is 10
// x is still 90
... won't change the value of x outside of bar.
For more info see:
W3Schools tutorial on function parameters
MDN's guide to functions (in particular the section on function scope)
As Rudolfs stated the rule is related to the variable in the outer scope. But it is not always true. For arrays & objects the rule is not applied
function modify(obj) {
obj.value = 'modified';
}
var objOuter = {value: 'original'};
console.log('The original value is ' + objOuter.value); //original
modify(objOuter);
console.log('The modified value is ' + objOuter.value); //modified
Playing with the Babel REPL to check how to use variable as object literals property name I found that
var y = { [X] : 'x', [Y] : 'y' };
// is translated to:
// ... _defineProperty definition ...
var _y;
var y = (_y = {}, _defineProperty(_y, X, 'x'), _defineProperty(_y, Y, 'y'), _y);
Now, I understand what is happening in _defineProperty, but someone could explain me what's happening inside the parentheses?
Are the statement inside () executed in order and only the last statement result is returned?
Yes. The comma operator evaluates all the expression and returns the result of the last one, e.g.
a(), b(), c(); // Calls `a`, `b` and `c`, and returns the value returned by `c`
In you case, the wrapping parentheses are also needed because of operator precedence.