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();
Related
Given an input file like
import { a } from 'b';
function x () {
a()
}
babel will compile it to
'use strict';
var _b = require('b');
function x() {
(0, _b.a)();
}
but when compiled in loose mode the function call is output as _b.a();
I've done some research into where the comma operator is added in the hope there was a comment explaining it.
The code responsible for adding it is here.
(0, _b.a)() ensures that the function _b.a is called with this set to the global object (or if strict mode is enabled, to undefined). If you were to call _b.a() directly, then _b.a is called with this set to _b.
(0, _b.a)(); is equivalent to
0; // Ignore result
var tmp = _b.a;
tmp();
(the , is the comma operator, see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comma_Operator).
The comma operator evaluates each of its operands (from left to right)
and returns the value of the last operand.
console.log((1, 2)); // Returns 2 in console
console.log((a = b = 3, c = 4)); // Returns 4 in console
So, let see an example:
var a = {
foo: function() {
console.log(this === window);
}
};
a.foo(); // Returns 'false' in console
(0, a.foo)(); // Returns 'true' in console
Now, in foo method, this is equal to a (because foo is attached to a). So if you call a.foo() directly, it will log false in console.
But, if you were call (0, a.foo)(). The expression (0, a.foo) will evaluate each of its operands (from left to right) and returns the value of the last operand. In other words, (0, a.foo) is equivalent to
function() {
console.log(this === window);
}
Since this function no longer is attached to anything, its this is the global object window. That's why it log true in console when call (0, a.foo)().
Calling a function in this roundabout way:
(throwAwayValueHere, fn)(args);
works like this:
The comma expression throwAwayValueHere, fn is evaluated: The comma operator evaluates its first operand, throws away that value, then evaluates its second operand and takes that value as its result.
That value is then called as a function, passing in the arguments.
Doing the call that way has an effect in two situations:
1. If the function is on an object property, e.g.:
(throwAwayValueHere, obj.fn)(args);
it calls the function without setting this to obj during the function call; instead, it's set to the default, either the global this value (window on browsers) or undefined in strict mode.
Example:
"use strict";
const obj = {
value: 42,
fn: function() {
console.log(`typeof this = ${typeof this}`);
if (typeof this === "object") {
console.log(`this.value = ${this.value}`);
}
}
};
// Normal call:
console.log(`obj.fn():`);
obj.fn();
// Indirect call:
console.log(`(0, obj.fn)():`);
(0, obj.fn)();
This is the reason Babel is doing it there: In the original code, the call was simply a(), which calls a with the default this value. Doing (0, _b.a)() does the same thing even though a is a property of _b.
2. If the function is eval, it makes it an indirect eval which means it's evaluated as though at global scope, rather than eval's default behavior of running arbitrary code from a string in local scope, giving it access to all in-scope variables.
Example:
"use strict";
let a = "global a";
function directEval() {
let a = "local a";
eval("console.log(`a = ${a}`);");
}
function indirectEval() {
let a = "local a";
(0,eval)("console.log(`a = ${a}`);");
}
console.log("direct:");
directEval();
console.log("indirect:");
indirectEval();
Given an input file like
import { a } from 'b';
function x () {
a()
}
babel will compile it to
'use strict';
var _b = require('b');
function x() {
(0, _b.a)();
}
but when compiled in loose mode the function call is output as _b.a();
I've done some research into where the comma operator is added in the hope there was a comment explaining it.
The code responsible for adding it is here.
(0, _b.a)() ensures that the function _b.a is called with this set to the global object (or if strict mode is enabled, to undefined). If you were to call _b.a() directly, then _b.a is called with this set to _b.
(0, _b.a)(); is equivalent to
0; // Ignore result
var tmp = _b.a;
tmp();
(the , is the comma operator, see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comma_Operator).
The comma operator evaluates each of its operands (from left to right)
and returns the value of the last operand.
console.log((1, 2)); // Returns 2 in console
console.log((a = b = 3, c = 4)); // Returns 4 in console
So, let see an example:
var a = {
foo: function() {
console.log(this === window);
}
};
a.foo(); // Returns 'false' in console
(0, a.foo)(); // Returns 'true' in console
Now, in foo method, this is equal to a (because foo is attached to a). So if you call a.foo() directly, it will log false in console.
But, if you were call (0, a.foo)(). The expression (0, a.foo) will evaluate each of its operands (from left to right) and returns the value of the last operand. In other words, (0, a.foo) is equivalent to
function() {
console.log(this === window);
}
Since this function no longer is attached to anything, its this is the global object window. That's why it log true in console when call (0, a.foo)().
Calling a function in this roundabout way:
(throwAwayValueHere, fn)(args);
works like this:
The comma expression throwAwayValueHere, fn is evaluated: The comma operator evaluates its first operand, throws away that value, then evaluates its second operand and takes that value as its result.
That value is then called as a function, passing in the arguments.
Doing the call that way has an effect in two situations:
1. If the function is on an object property, e.g.:
(throwAwayValueHere, obj.fn)(args);
it calls the function without setting this to obj during the function call; instead, it's set to the default, either the global this value (window on browsers) or undefined in strict mode.
Example:
"use strict";
const obj = {
value: 42,
fn: function() {
console.log(`typeof this = ${typeof this}`);
if (typeof this === "object") {
console.log(`this.value = ${this.value}`);
}
}
};
// Normal call:
console.log(`obj.fn():`);
obj.fn();
// Indirect call:
console.log(`(0, obj.fn)():`);
(0, obj.fn)();
This is the reason Babel is doing it there: In the original code, the call was simply a(), which calls a with the default this value. Doing (0, _b.a)() does the same thing even though a is a property of _b.
2. If the function is eval, it makes it an indirect eval which means it's evaluated as though at global scope, rather than eval's default behavior of running arbitrary code from a string in local scope, giving it access to all in-scope variables.
Example:
"use strict";
let a = "global a";
function directEval() {
let a = "local a";
eval("console.log(`a = ${a}`);");
}
function indirectEval() {
let a = "local a";
(0,eval)("console.log(`a = ${a}`);");
}
console.log("direct:");
directEval();
console.log("indirect:");
indirectEval();
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.
Given an input file like
import { a } from 'b';
function x () {
a()
}
babel will compile it to
'use strict';
var _b = require('b');
function x() {
(0, _b.a)();
}
but when compiled in loose mode the function call is output as _b.a();
I've done some research into where the comma operator is added in the hope there was a comment explaining it.
The code responsible for adding it is here.
(0, _b.a)() ensures that the function _b.a is called with this set to the global object (or if strict mode is enabled, to undefined). If you were to call _b.a() directly, then _b.a is called with this set to _b.
(0, _b.a)(); is equivalent to
0; // Ignore result
var tmp = _b.a;
tmp();
(the , is the comma operator, see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comma_Operator).
The comma operator evaluates each of its operands (from left to right)
and returns the value of the last operand.
console.log((1, 2)); // Returns 2 in console
console.log((a = b = 3, c = 4)); // Returns 4 in console
So, let see an example:
var a = {
foo: function() {
console.log(this === window);
}
};
a.foo(); // Returns 'false' in console
(0, a.foo)(); // Returns 'true' in console
Now, in foo method, this is equal to a (because foo is attached to a). So if you call a.foo() directly, it will log false in console.
But, if you were call (0, a.foo)(). The expression (0, a.foo) will evaluate each of its operands (from left to right) and returns the value of the last operand. In other words, (0, a.foo) is equivalent to
function() {
console.log(this === window);
}
Since this function no longer is attached to anything, its this is the global object window. That's why it log true in console when call (0, a.foo)().
Calling a function in this roundabout way:
(throwAwayValueHere, fn)(args);
works like this:
The comma expression throwAwayValueHere, fn is evaluated: The comma operator evaluates its first operand, throws away that value, then evaluates its second operand and takes that value as its result.
That value is then called as a function, passing in the arguments.
Doing the call that way has an effect in two situations:
1. If the function is on an object property, e.g.:
(throwAwayValueHere, obj.fn)(args);
it calls the function without setting this to obj during the function call; instead, it's set to the default, either the global this value (window on browsers) or undefined in strict mode.
Example:
"use strict";
const obj = {
value: 42,
fn: function() {
console.log(`typeof this = ${typeof this}`);
if (typeof this === "object") {
console.log(`this.value = ${this.value}`);
}
}
};
// Normal call:
console.log(`obj.fn():`);
obj.fn();
// Indirect call:
console.log(`(0, obj.fn)():`);
(0, obj.fn)();
This is the reason Babel is doing it there: In the original code, the call was simply a(), which calls a with the default this value. Doing (0, _b.a)() does the same thing even though a is a property of _b.
2. If the function is eval, it makes it an indirect eval which means it's evaluated as though at global scope, rather than eval's default behavior of running arbitrary code from a string in local scope, giving it access to all in-scope variables.
Example:
"use strict";
let a = "global a";
function directEval() {
let a = "local a";
eval("console.log(`a = ${a}`);");
}
function indirectEval() {
let a = "local a";
(0,eval)("console.log(`a = ${a}`);");
}
console.log("direct:");
directEval();
console.log("indirect:");
indirectEval();
Is there a way to get the Function object, while the function is executing?
I am assigning properties to my function, and want to access them. "this" doesn't help. Something like:
a.b=function(){...code...};
a.b.c=100;
I want to access a.b.c from the code in the function, without knowing its own name. "this" refers to a. How can get b?
I tried binding the function to his own object, but I couldn't.
Thank you.
I'm adding this example, I have to repeat after several different "theString" and "someSpecificValues":
Object.defineProperty(theObject, theString, {get: function(...){...}.bind(theObject, someSpecificValues), configurable: true});
You can use a named function expression for this:
var a = {};
a.b = function myFunc() {
console.log(myFunc.c);
};
a.b.c = 100;
a.b();
It allows code inside the function to access the function itself, but does not add the identifier to the enclosing scope.
Edit: Here is a more elaborate example of how the name myFunc only exists within the function:
var a = {};
a.b = function myFunc() {
console.log(myFunc.c);
};
a.b.c = 100;
a.d = function myFunc() {
console.log(myFunc.c);
};
a.d.c = 300;
a.b(); // logs 100
a.d(); // logs 300
console.log(typeof myFunc); // logs "undefined"
// create a myFunc variable
var myFunc = function() {
console.log("nooooooo!!!!");
};
a.b(); // STILL logs 100. the myFunc variable in this scope
// has no effect on the myFunc name that a.b uses
function callFunc(theFunc) {
theFunc();
}
callFunc(a.d); // STILL logs 300
// ===========================
function returnNamedFunction () {
return function myFunc() {
console.log(myFunc.c);
};
}
var iGotAFunction = returnNamedFunction();
iGotAFunction.c = 700;
iGotAFunction(); // logs 700
In the case when you cannot use a named function expression, e.g. when you are using .bind() on it, then an IIFE will suffice most of the time:
var myObj = {};
myObj.theFunc = (function () {
var f = function (arg1, arg2) {
console.log(this.theProp);
console.log(arg1);
console.log(arg2);
console.log(f.lista);
}.bind(myObj, "A!");
return f;
})();
myObj.theProp = "B!";
myObj.theFunc.lista = [1, 2, 3];
myObj.theFunc("C!");
There are two ways to get current function.
One is "almost deprecated" usage of arguments.callee. In function body it always refers to this function.
var a = {};
a.b = function () {
console.log(arguments.callee.c);
};
a.b.c = 100;
a.b();
arguments.callee is forbidden in strict mode. Reference.
The second one is using named function expression as JLRishe pointed.
arguments.callee pros and cons
Advantages:
it can be safely used with bound functions (arguments.callee refers to bound function)
it can be used with functions created using new Function
Disadvantages:
it can slow your program due to disabling certain optimisations
it's considered as almost deprecated
it can't be used in strict mode
Named function expression pros and cons
Advantages:
it's faster than arguments.callee
it's easier to understand how it works
Disadvantages:
it won't work as expected with bound functions (functionName will refer to original function, not bound one)
it can't be used in functions created with new Function