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();
Related
When we take a rest parameter in the parameter list and try to redefine the arguments using arguments[0]=99999 parameter a is not re-defined
//function defined
function bar(a, b, c, ...args) {
arguments[0] = 99999;
console.log(arguments[0]); //99999
document.write(a); //20
}
//function call
bar(20);
When you use rest syntax, you do not have what the specification calls a "simple parameter list", which you can find here:
14.1.13 Static Semantics: IsSimpleParameterList
(If the syntax is) FormalParameters:FormalParameterList, FunctionRestParameter
Return false.
And, as you can see in 9.2.10 FunctionDeclarationInstantiation, when you don't have a IsSimpleParameterList, the following is run:
If argumentsObjectNeeded is true, then
a. If strict is true or if simpleParameterList is false, then
Let ao be CreateUnmappedArgumentsObject(argumentsList).
b. Else
Let ao be CreateMappedArgumentsObject(func, formals, argumentsList, envRec).
If you're in strict mode or there are any rest parameters, the arguments object is not linked to the argument names, as described in CreateUnmappedArgumentsObject. Otherwise, a "mapped" arguments object is created, in which case reassigning one of the arguments properties will result in the parameter being reassigned.
Here's an example showing that having the rest parameter prevents the link between arguments and the formal parameters, but doesn't make the function strict:
function looseFunction(a) {
arguments[0] = "changed";
console.log(arguments[0]); // "changed"
console.log(a); // "changed", because `arguments` is linked to `a`
}
function looseFunctionWithoutLink(a, ...rest) {
arguments[0] = "changed";
console.log(arguments[0]); // "changed"
console.log(a); // "original", because `arguments` is not linked
// to `a`
foo = 42; // Not an error (it would be in strict mode)
// assigning to an undeclared identifier
console.log(foo); // 42
}
looseFunction("original");
looseFunctionWithoutLink("original");
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();
Please look at the code below and explain: what am I doing wrong?
function doStuff(a, b){
return a + b + 1 ;
}
var myContext = {
c: 1,
d: 3
};
// myContext = this (result => 5)
doStuff.call(myContext,myContext.c,myContext.d)
// ... so why doesn't the below work? (result => NaN)
doStuff.call(myContext,this.c,this.d)
// To make the above work, i must replace "this" with "myContext" (result => 5)...
doStuff.call(myContext,myContext.c,myContext.d)
// ...which is no different to...
doStuff(myContext.c,myContext.d)
// ...so what was the point of call() method?
Am I being thick?
The main point of call is to set the value of this within the function. Since your doStuff doesn't use this within the function, using call with it is pointless.
Here's an example where it matters:
function doStuff(a, b) {
return this.sum(a, b);
}
var obj = {
sum: function(a, b) {
return a + b;
}
};
console.log(doStuff.call(obj, 3, 4)); // 7, because `obj` has a `sum` property
console.log(doStuff(3, 4)); // Fails with an error that `this.sum` is not a function
so why doesn't the below work? (result => NaN)
doStuff.call(myContext,this.c,this.d)
Because this.c is evaluated before the call to doStuff, using whatever the current this value is. How you're probably calling that code, this is (in loose mode) the global object (the window object, on browsers), which probably doesn't have a c or d property. (In strict mode, again making assumptions about how you're calling that, you'd get an exception, because this is undefined and you can't retrieve properties from undefined.)
.call and .apply are Function methods that 'manipulate' what this means inside a function.
doStuff.call(myContext, myContext.c, myContext.d):
here you've set myContext as context for doStuff function
and you may refer to it inside by using this,
and you've passed two arguments to it: myContext.c, myContext.d,
it works as you've intended...
doStuff.call(myContext, this.c, this.d):
again myContext is context for doStuff()
but you've passed .c and .d properties of what this points to
in context in which it appears (global object, window) in your case.
So doStuff's context is myContext, and parameters are 2 undefineds,
because this === window in context where you are calling the function,
and you are passing .c and .d properties of global into the function.
you are actually getting this: return undefined + undefined + 1; (NaN)
if you redefine 'doStuff' like this:
function doStuff () {
return this.a + this.b + 1;
// here it looks whatever this is set to
// by `.call()` or `.apply()` methods
}
and call it like this:
var sum = doStuff.call(myContext);
// notice that using `.call` here
// means that 'myContext' is manualy set
// as `this` value inside the function above
// and is refered to by dynamic variable 'this'
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();