What's the reason for using such syntax (0, _.Em)();
Asked Answered
D

1

10

While investigating google plusone scripts, I've seen following syntax many times:

(0, _.Em)();

Assuming _.Em is a function the statement above would result in calling that function, that's pretty obvious. If, on the other hand, it would be undefined, wouldn't the result be the same as doing simply _.Em() ?

Can anyone shed a light on what's idea behind using such syntax?

Dicky answered 16/3, 2012 at 10:17 Comment(2)
see also Does the comma operator influence the execution context in Javascript?Ascensive
see also Why does google main page use (0, obj.func)(args) syntax?Ascensive
S
9

Basically, this syntax allows to call _.Em() in the context of the window object instead of _.

Assuming you have this code:

Foo = function() {
    this.foo = "foo";
};

Foo.prototype.Em = function() {
    alert(this.foo);
};

var _ = new Foo();

Issuing _.Em() will result in Em() being called in the context of _. Inside the function, the this keyword will refer to _, so foo will be printed.

Issuing (0, _.Em)() decouples the method call from the object and performs the call in the global context. Inside the function, the this keyword will refer to window, so undefined will be printed, since window does not have a foo property.

You can test the difference between the two syntaxes in this fiddle.

Swipe answered 16/3, 2012 at 10:43 Comment(9)
Because evaluating _.Em then applying the call operator is not the same as calling _.Em() directly. Evaluating _.Em independently returns a "free function", for lack of a better term, and that function will not be tied to the _ object anymore. The same result can be obtained by writing var f = _.Em; f();. In both cases, the this keyword will refer to window instead of _ inside the function.Quagga
+1, good answer. I'd like to add that this is perhaps the most pointless form of syntactic sugaring that I've ever seen in JS. (0, )() is exactly the same length as .call(), so there's no byte saving unless you remove the space after the comma (and even then it's only one). At the cost of confusing unsuspecting developers, it really doesn't seem worth using syntax like this.Norward
@Andy, I think it has to do with strict mode. In that mode, _.Em.call() without arguments will call Em() in the context of undefined, not window, and _.Em.call(window) is arguably longer than (0, _.Em)().Quagga
So what is the (0, _.Em) doing to isolate Em from _? I know that (1,2,3) gives 3 as the result. So it seems like (_.Em) and (0, _.Em) should be equivalent, but (_.Em)() behaves just like _.Em() so they are clearly not the same. Also, fwiw in Chrome, the fiddle example reported undefined, not window.Betoken
@FrédéricHamidi: you're completely right, I had forgotten about that. I'd still rather use .call(this), but I guess scripts like Google's Plus One script needs to save all the bytes it can.Norward
@Devon, yup, that's not just Chrome. As I said in my answer, undefined will be printed because this.foo is evaluated, not just this (which would print [object Window]). Now, (0, _.Em) is not the same as (_.Em), as the comma operator is involved, so _.Em is returned as a free function before the call operator is seen. (_.Em) is equivalent to _.Em because a single expression between parentheses is always equivalent to that expression.Quagga
@Andy, ah, but what if the caller does not operate in the global context? In that case this won't refer to window in the caller, and .call(this) will relay that object as the callee's context.Quagga
@FrédéricHamidi: yes, of course, there's that too. I was simply stating that I would still choose the more verbose option for clarity.Norward
Actually it's in the context of undefined, only in sloppy-mode function it falls back to the global object.Ascensive

© 2022 - 2024 — McMap. All rights reserved.