In transpiled code, you'll often notice a pattern that looks like this:
i.default.createElement("p", {
dangerouslySetInnerHTML: {
__html: (0, $.sanitize)(n.desc) // What's the 0?
},
__self: this,
__source: {
fileName: S,
lineNumber: 328,
columnNumber: 13
}
})
With the line __html: (0, $.sanitize)(n.desc)
, we see 2 curious things:
- Why do the parens in
(0, $.sanitize)
have two expressions? - Why is there a seemingly useless number
0
present?
Let's explain:
(0, $.sanitize)(n.desc)
simply calls $.sanitize
with n.desc
as an argument. In other words, this is the same as: $.sanitize(n.desc)
.
Inside the parenthesis, the comma operator evaluates all expressions in sequence but only returns the value of the last expression.
function foo() {
return ('a', 'b', 1+3);
}
foo(); // returns 4
So by adding the 0
before the comma ,
in (0, $.sanitize)(n.desc)
the transpiler is using the comma operator to create an expression where the value 0
is discarded (so it could technically be any other expression without side effects), but the function $.sanitize
is still executed. This ensures that the $.sanitize
function is treated as a standalone expression and evaluated before the function call (...)(...)
is made.
This pattern shows a specific transformation in the bundled output by ensuring the fn
in (0, fn)(arg)
is invoked with a this
value of undefined
:
'use strict';
const spellbook = {
title: 'Arcanum',
enchant(weapon) {
console.log(`Enchanting ${weapon}`);
console.log(this);
}
};
// Will log "this" as {title: 'Arcanum', enchant: ƒ}
spellbook.enchant('sword');
// Will log "this" as undefined
(0, spellbook.enchant)('sword');
/*
Note that "this" will refer to the global object in
non-strict mode or "undefined" in strict mode.
*/
This transformation ensures that exported functions are not called with a different this
context which will lead to incorrect function behavior.
Even though not all functions will need to access this
, this transformation is applied regardless so the transpiler does not need to waste time analyzing wether or not the code accesses it.
Instead of (0, fn)(arg)
, could we have called the function in this manner: (fn)(arg)
?
No, because the the parentheses around fn
would be interpreted simply as a grouping operator rather than the comma operator, and it is the comma operator that triggers the behavior of calling the function without any specific context.
I always believed the 0
in these patterns was an index to another function in the code bundle, but it's not! It’s a transformation pattern used when exporting functions to ensure that they are not unintentionally bound to a different context. ✨
Special thanks to user j4k0xb and their very helpful explanation which prompted a better explanation on my part.
Yo! I post byte-sized tips like these often. Follow me if you crave more! 🍿 JS Bits Blog
Top comments (3)
what? 🤔
The actual reason is to ensure that the receiver/
this
isundefined
when calling. Maybe you meant that but I didnt really see it explained in the article...Not all functions access
this
but to avoid analyzing (slow) it gets applied anywaysReal example where transpiling to
obj.fn(args)
instead of(0, obj.fn)(args)
resulted in an error: github.com/es-shims/Promise.allSet...@j4k0xb, thank you so much for this explanation. My interpretation was way too vague so I really appreciate your insight! I've edited this article with hopefully a better explanation.
Fascinating!!