So... ES6¹ (which happens to be standardized a few hours ago) brings default parameters for functions similar to those in PHP, Python etc. I can do stuff like:
function foo (bar = 'dum') {
return bar;
}
foo(1); // 1
foo(); // 'dum'
foo(undefined); // 'dum'
MDN says that the default value for the parameter is evaluated at call time. Which means each time I call the function, the expression 'dum' is evaluated again (unless the implementation does some weird optimizations which we don't care about).
My question is, how does this play into this?
let x = {
foo (bar = this.foo) {
return bar;
}
}
let y = {
z: x.foo
}
x.foo() === y.z(); // what?
The babel transpiler currently evaluates² it as false, but I don't get it. If they are really evaluated at call time, what about this:
let x = 'x from global';
function bar (thing = x) {
return thing;
}
function foo () {
let x = 'x from foo';
return bar();
}
bar() === foo(); // what?
The babel transpiler currently evaluates³ it as true, but I don't get it. Why does bar not take the x from foo when called inside foo?
1 - Yes I know it is ES2015.
2 - Example A
3 - Example B
My question is, how does this play into this? I don't get it. Are they are really evaluated at call time?
Yes, the parameter initializers are evaluated at call time. It's complicated, but the steps are basically as follows:
A new execution context is established on the stack,
with a new environment in the "closure scope" of the called function
If necessary, it's thisBinding is initialised
Declarations are instantiated:
Mutable bindings for the parameter names are created
If necessary, an arguments object is created an bound
The bindings are iteratively initialised from the arguments list (including all destructurings etc)
In the course of this, initialisers are evaluated
If any closures were involved, a new environment is inserted
Mutable bindings for the variables declared in the function body are created (if not already done by parameter names) and initialised with undefined
Bindings for let and const variables in the function body are created
The bindings for functions (from function declarations in the body) are initialised with instantiated functions
Finally the body of the function is evaluated.
So parameter initialisers do have access to the this and the arguments of the call, to previously initialised other parameters, and everything that is in their "upper" lexical scope. They are not affected by the variables declared in the function body (though they are affected by all the other parameters, even if in their temporal dead zone).
what about this:
function bar (thing = x) {}
{
let x = 'x from foo';
return bar();
}
I don't get it. Why does bar not take the x from foo when called
inside foo?
Because x is a local variable that bar does not have access to. We're so lucky that they are not dynamically scoped! The parameter initialisers are not evaluated at the call site, but inside the called function's scope. In this case, the x identifier is resolved to the global x variable.
When they say "evaluated at call time", I think they're referring to a call-by-name expression. Here's how babel outputs your third example:
'use strict';
var x = 'x from global';
function bar() {
var thing = arguments[0] === undefined ? x : arguments[0];
return thing;
}
function foo() {
var x = 'x from foo';
return bar();
}
bar() === foo(); // what?
Since var x is inherited within the lexical scope of bar from the global scope, that is the scope in which it is used.
Now, consider the following:
let i = 0;
function id() {
return i++;
}
function bar (thing = id()) {
return thing;
}
console.info(bar() === bar()); // false
Which transpiles to
"use strict";
var i = 0;
function id() {
return i++;
}
function bar() {
var thing = arguments[0] === undefined ? id() : arguments[0];
return thing;
}
console.info(bar() === bar()); // false
Notice how here, id is called inside the function, rather than being cached and memoized at the time the function is defined, hence the call-by-name rather than call-by-value.
So the behavior is actually correct in your second example. There is no y.foo and since this is dynamically scoped in Javascript (i.e. it varies based on the receiver of a given function invocation), when y.z() looks for this.foo, it will look for it in y, so y.z() will return undefined, while x.foo() will just return the foo function itself.
If you do want to bind to the receiver you can bind foo to x when you assign it. Then it should work as expected.
Sorry if any of this is unclear; let me know in the comments and I'd be happy to clarify! :)
Related
I was reading Kyle Simpson book: https://github.com/getify/You-Dont-Know-JS/blob/1st-ed/scope%20%26%20closures/ch4.md#functions-first.
But I don't fully understand this line "Notice that var foo was the duplicate (and thus ignored) declaration, even though it came before the function foo()... declaration, because function declarations are hoisted before normal variables."
Let's say this is the code:
console.log(foo); // The output is: foo() { return 2; }
function foo() {
return 1;
}
function foo() {
return 2;
}
var foo = 3;
I want to visualize what would be the output in JS Engine after Memory Creation phase. Will it be like this?
function foo() {
return 2;
}
console.log(foo);
If yes, why var foo = 3; was ignored? There is no duplicate for var in the snippet. If no, can anyone please help me visualize what would be the output in JS Engine after Memory creation phase?
Thanks
I think the text refers to
// scope creation
var foo; // the name was declared. Thrice.
foo = undefined; // from the var
foo = function foo() { return 1 }; // from the first declaration
foo = function foo() { return 1 }; // from the second iteration
// execution
console.log(foo);
;
;
foo = 3;
where foo is initialised with undefined due to the var foo declaration, but then gets overwritten by the initialisation value from the function foo declaration which takes precedence - regardless of the order in which the declarations appeared in the code, function declarations overrule var declarations.
Function declarations
Declare a variable with the same name as the function (this is hoisted)
Assign the function to that variable (this is hoisted)
var statements with associated assignments
Declare a variable with that name (this is hoisted)
Assign the value to that variable (this is not hoisted)
var foo = 3 is not ignored.
The variable declaration (var foo) is ignored because the earlier function declarations already declared that variable. (i.e. because it duplicates the declaration of foo).
foo = 3 is not ignored and does assign 3 to foo … it just does so after your console.log statement runs because it isn't hosited.
I am trying to understand what is going "under the hood". Other questions dont explain the mechanics I am interested it. Please see code under.
I am looking for steps like this:
(p.foo = o.foo)(); expresion
Value of o.foo is hold somewhere and is not yet related to p obj?
Value of o.fooget executed?
p.foo gets the value on o.foo?
is this right? if yes then need more info in step 2 on how, where and why...
function foo() {
console.log( this.a );
}
var a = 2;
var o = { a: 3, foo: foo };
var p = { a: 4 };
o.foo(); // 3
(p.foo = o.foo)(); // we get 2 and not 4”
There is nothing special happening here.
This line
(p.foo = o.foo)();
is equivalent to executing below function in global scope(this refers to window) where value of a is 2 which gets printed.
function foo() {
console.log( this.a );
}
following assignment returns function definition
p.foo = o.foo
its not executing foo on p or o object. its just adding property foo on p object and assigning it that function.
Hence , you end up executing code in global scope
(function () {
console.log( this.a );
})()
p.foo = o.foo is an expression. All expressions resolve to a value. In this case, it's the function foo().
So basically, when you call this code, you not only assign a new value to p.foo (because it so happens that the expression contains the assignment operator =) -- you also get back the global function foo as a result of evaluating the expression.
You then call the function inline in the window context, where a equals to 2.
i was working with closures and i came to this code:
doit = true;
aww = function() {
if(doit) {var foo; doit = false;} else {}
foo = 0; foo += 1; return foo;
}
doit is a variable that aww function uses to only define/delcare foo once.
when i execute aww for first time, it delcares foo as a local variable (and set to not declare it on future calls) then it assigns foo to 0 and increases it by 1. and returns it (1)
I expect that, next time i call aww the foo variable is not declared first, so that the aww function should first create foo and make it global, because it has not been declared and it is being assigned to 0 without any var before it. but what really happens differ from my expectation.
foo does not become global, it is still local.
Is it true to assume that function's scopes remain after the function execution ends?
Or if not, is it true to say that only lists of localy declared variables is saved somewhere?
Ive read about closures, but did not understand them exactly. answers to this question make closures really clearer to me than before. Thx.
Your function:
aww = function() {
if(doit) {var foo; doit = false;} else {}
foo = 0; foo += 1; return foo;
}
is interpreted as if it were written like this:
aww = function() {
var foo;
if(doit) {doit = false;} else {}
foo = 0; foo += 1; return foo;
}
The function will always return 1, because foo is always set to 0 and then 1 by the function. The variable declaration is treated as if it occurred at the top of the function.
The reference to doit is the part of this that has to do with a closure. Because that variable is external to the function, the function will toggle its value to false the first time it's called, and then won't change it again. It could access doit and rely on its value remaining the same between calls, unless something else has access to doit and makes changes.
The variable foo, on the other hand, is a simple local variable to the function. Every call to the function will create a brand new foo, and that one will have no relationship to any foo that came before it.
Let's consider simple singleton implementation:
var singleton = function (Constructor) {
var singleton;
return function () {
if (!singleton) {
singleton = new Constructor();
}
return singleton;
};
};
We could move declaration of singleton variable to arguments:
var singleton = function (Constructor, singleton) {
return function () {
if (!singleton) {
singleton = new Constructor();
}
return singleton;
};
};
So I simply curious about side effects.
One more example:
var counter = (function (i) {
return function () {
i = (i || 0) + 1;
return i;
};
}());
We could move declaration of singleton variable to arguments
First, let's make it possible to talk about this without tying outselves up in knots by using the same symbol (singleton) for two completely different things within the same few lines of code.
Here's your seecond example renamed:
var singleton = function (Constructor, instance) {
return function () {
if (!instance) {
instance = new Constructor();
}
return instance;
};
};
If you did that, then calling the singleton function with two arguments would specify instance, making passing Constructor in pointless — Constructor would never be called by singleton (again, if you passed in two args [and the second arg was truthy]). So it would be a bit odd to do that.
But you asked about side-effects. There are no external effects involved if you assign to a formal argument within the function. It doesn't, for instance, have any effect outside the function:
function foo(arg) {
arg = 67;
}
var a = 42;
foo(a);
console.log(a); // Still 42
However, assigning to an argument rather than to a local variable does, in non-strict mode, have a small cost, because there's overhead involved: It relates to the magic arguments pseudo-array. In non-strict mode, there's a link between the formal arguments of a function and the arguments pseudo-array:
function foo(a, b) {
console.log(a);
console.log(b);
b = 42;
console.log(arguments[1]);
}
If we call that like this:
foo(1, 2);
we see
1
2
42
Note the magic: Assigning to the formal argument b updated the pseudo-array arguments. (It works the other way, too.) That link has a small but real runtime cost.
(In strict mode, there is no link, for this very reason.)
As far as closure goes, there is no difference in the two implementations. However, in first example, singleton is not settable by the caller.
I will choose between the two implementation, just based on whether singleton could have been created outside these example functions or not. If it can be, use model 2 otherwise use model 1
I have done a fair amount of research but have not been able to find any answers, to what seems like a simple question:
I want to associate a property with a function (to use as a static variable in this function), like so:
function foo() {
if (!foo.counter) {
foo.counter = 1;
}
else {
foo.counter++
}
// rest of the function code goes here...
}
If I change the name of the function later, I don't want to have to change references to it inside function definition.
So, is there a way to refer to the currently executing function? (other than arguments.callee which is now deprecated). A keyword like thisFunction?
If not, what is the reason for not having something like this?
I do not know of an identifier or keyword like thisFunction (besides arguments.callee) that JavaScript exposes, but one way to achieve the same effect is to name the function foo in a closure, and return foo from that closure. That way you can always use the name foo within foo to refer to itself, regardless of what variable it's assigned to by your program:
var fn = (function() {
function foo() {
if (!foo.counter) {
foo.counter = 1;
}
else {
foo.counter++
}
// rest of the function code goes here...
}
return foo;
})();
function foo() {
if (typeof foo.counter=="undefined") {
foo.counter = 1;
}
else {
foo.counter++
}
return foo;
}
var x = new foo();
alert(x.counter); //1
var y = new foo();
alert(x.counter); //2
If you don't need to refer to the counter outside of the function, this will work. Instead of setting a variable of the function itself, you define a variable outside of the function and let the function modify that instead. Every time you call makeFoo, it makes a new variable x and returns a function using x. Every time you call the returned function, it still refers to the same x created by makeFoo. Additionally, this entirely encapsulates the variable, ensuring that almost nothing outside of the returned function can change it (it definitely won't be changed by most things outside of the returned function, but there is enough funky javascript reflection that I can't guarantee that nothing will change it). Certainly, moving the function between variables won't affect the counter (ie x=makeFoo();y=x;x=null;).
function makeFoo() {
var x=0;
return function () {
x++;
return x;
}
}
//different counters
x=makeFoo();
y=makeFoo();
x();//returns 1
y();//returns 1
x();//returns 2
y();//returns 2
//same counter
x=makeFoo();
y=x;
x();//returns 1
y();//returns 2
x();//returns 3
y();//returns 4