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.
Related
This question already has an answer here:
Scope of Default function parameters in javascript
(1 answer)
Closed 4 years ago.
When I try to run the foo function defined in this snippet I get a ReferenceError since b is not defined.
var b = 3;
function foo( a = 42, b = a + b + 5 ) {
// ..
}
foo()
This looks like a TDZ error because b has been defined in the outer scope but it's not yet usable in the function signature as a right-hand-side value.
This is what I think the compiler should do:
var b;
function foo(..) { .. }
// hoist all functions and variables declarations to the top
// then perform assignments operations
b = 3;
foo();
//create a new execution environment for `foo`
// add `foo` on top of the callstack
// look for variable a, can't find one, hence automatically create a
`var a` in the local execution environment and assign to it the
value `42`
// look for a `var b` in the global execution context, find one, use
the value in it (`3`) as a right-hand-side value.
This shouldn't raise a ReferenceError. Looks like this is not what happens here.
Can someone explain in what actually does the compiler do and how it processes this code?
On every function call, the engine evaluates some prologue code, which contains formal parameters, declared as let vars and initialized with their actual values or default expressions, if provided:
var b = 3;
function foo( ) {
let a = <actual param for a> OR 42;
let b = <actual param for b> OR a + b + 5;
// ..
}
Since b in a function is lexical (let), it's not possible to access its value before initialization. Hence the ReferenceError.
Note that this is a call-time error, so the following compiles fine:
var b = 1
function foo(b=b) {
console.log(b)
}
The error happens when you actually call the function:
var b = 1
function foo(b=b) {
console.log(b)
}
foo()
and only when the engine actually evaluates the faulty default expression:
var b = 1
function foo(b=b) {
console.log(b)
}
foo(7)
ECMA standard reference: FunctionDeclarationInstantiation, p.21:
For each String paramName in parameterNames, do
...Perform ! envRec.CreateMutableBinding(paramName, false).
The function arguments somewhat works like 'let'.
We cannot access variable created using 'let' before they are declared .i.e variables created using 'let' are not hoisted.
This happens because if the we are declaring the variable in local scope , it cannot access the global scope variable(Unless 'this' is used )
Your code can be fixed by this -
var b = 3;
function foo( a = 42, b = a + this.b + 5 ) {
// default binding. In this case this.b = global var
}
foo()
you can also see this error if you do this.
let variable = variable;
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! :)
david sharif made a JS quiz which pretty much looks like-
var foo=1;
function bar(){
return foo;
foo=10;
function foo(){}
var foo =5;
}
typeof bar();//?
In my understanding, functions are hosited first and then variable declared inside. the hosited form of the function would be something like (correct me if i am wrong)-
var foo=1;
function bar(){
function foo(){}
var foo;
return foo;
foo=10;
foo =5;
}
typeof bar();//?
why typeof bar() is function not undefined?
Is this because of, at the time of function execution, it finds the first foo (which is a function) and returns happily without continuing search. Or something else?
Appreciate your time.
I found the answer in David Shariff blog.
"Shamelessly copied from his blog"-
Even though foo is declared twice, we know from the creation stage that functions are created on the activation object before variables, and if the property name already exists on the activation object, we simply bypass the declaration.
Therefore, a reference to function foo() is first created on the activation object, and when we get interpreter gets to var foo, we already see the property name foo exists so the code does nothing and proceeds.
If this sound like greek, read the whole blog
The Function Declaration "shadows" the var statement.
Paste this in to your console:
var foo = function(){}
var foo
typeof foo
This is how the code "looks like" to the Interpreter after compiletime:
var bar = function bar(){
var foo = function foo(){}
foo
return foo;
// never reached
foo = 10;
foo = 5;
}
var foo;
foo = 1
typeof bar();//"function"
Function declarations are evaluated upon entry into the enclosing scope, before any step-by-step code is executed. The function's name (foo) is added to the enclosing scope (technically, the variable object for the execution context the function is defined in).
Example 1
From this example wecan see that even we are declaring foo after the return statement, we can still get foo returned.
function bar() {
return foo;
function foo() {} // this will get initialized before the return statement.
}
console.log(typeof bar()); // this will be function
Example 2
From this example we can see that if we are declaring our variables in a normal way after the return statement, they will not execute(assigned).
Declare function after return.
function bar() {
return foo;
var foo = function() {} // this will not get assigned
}
console.log(typeof bar()); // this will be undefined
Declare number after return
function bar() {
return foo;
var foo = 12; // this will not get assigned
}
console.log(typeof bar()); // this will be undefined
Example 3
Now let's walk through a messier example line-by-line.
function bar() {
return foo;
function foo() {} // <-- initialize foo as a function
var foo = 11; // <-- this will not get assigned
}
console.log(typeof bar()); // function
I hope the above example will not only answer you question but also give you a better understanding of:
var foo = function() {} vs function foo() {}
What exactly have I attached as an argument to function #1? A new object, a string, something else? It does not look like a reference, because changing foo has no effect. From what I can see, bar1, bar2 and bar 3 behave exactly the same. Is there a difference between any of them?
foo = 5;
bar1 = (function(){return fooBar(foo);}()); // <--- #1
bar2 = (function(){return fooBar("5");}()); // <--- #2
bar3 = (function(){return fooBar(5);}()); // <--- #3
alert(bar1);
alert(bar2);
alert(bar3);
edit -> as requested, the fooBar function
var p = 3;
function fooBar(n)
{
return (p*n);
}
foo = 5;
bar = (function(){return fooBar(foo);}());
alert(bar);
edit 2
function fooBar(n)
{
return (n[0]+n[1]);
}
foo = [20, 15];
bar = (function(){return fooBar(foo);}());
foo = [4, 565];
alert(bar); // 35
bar1 becomes the result that is returned from fooBar(foo) which is fooBar(5) and evaluates to 3*5, finally 15
And because these 3 statements are written as self-invoking functions function(){ .... }(), changing foo after that bar1 = ... statement will have no effect, since the value of foo has already been passed into it.
Since your foo variable is global (defined without the var keyword / defined in an outer scope) it is available inside your anonymous function as well.
Your fooBar function is however undefined so hard to tell what it's doing.
Also, it's better to use console.dir() to dump variables as it also shows the type.
Edit: Apparently console.dir() does not show the type (in Chrome at least)
I am curious as to why this works:
var c = {
d: function myFunc() {
console.log(this === window);
}
};
var a = {
b: function() {
console.log(this === a);
(0,c.d)();
c.d();
}
};
a.b();
Console output:
True
True
False
So it seems to be that (0, c.d)() is the same as c.d.call(window), but I cannot seem to find much about why or how this works. Can anyone explain?
From: Closure Compiler Issues
Fiddle: http://jsfiddle.net/wPWb4/2/
If you write multiple expressions separated by a comma (,), then all expressions will be evaluated, but you will end up with the value of the last expression:
var x = (1,2,3);
console.log(x); // this will log "3"
Now (0,c.d) is an expression that will return the function c.d, but now c is not the this of the function anymore. This means that this will point to the global object (window), or remain undefined in strict mode. You would get the same effect with any of these:
var f = function(x) { return x; };
f(c.d)();
Or just
var f = c.d;
f();
The expression (0, myFunc) is equivalent to just myFunc. The comma operator takes multiple expressions, evaluate them, and returns the last expression, so (0, myFunc) returns just myFunc.
Now, in your b method, this is equal to a because b is attached to a. In other words, b can be called by a.b(). However, myFunc is not attached to a (you can't call myFunc by a.myFunc()), so in myFunc, this can't be a. In fact, myFunc is called without setting its this so it defaults to the global object window (or remains undefined in strict mode), and therefore its this is equal to window.
You might be wondering what the point of the (0, listeners[i])() is, then, when they could have just written listeners[i](). Consider this code that creates an array with two elements, both functions:
var listeners = [
function () { alert(this); },
function () { alert(this); }
];
When you call listeners[0](), then in the listeners[0] function, this is equal to listeners, because 0 is attached to listeners, just like how b was attached to a. However, when you call the function by (0, listeners[0])(), listeners[0] is "decoupled" from the listeners array.* Since the decoupled function no longer is attached to anything, its this is the global object window.
*It's as if you wrote:
var temp = listeners[0];
temp();