DEV Community

Cover image for Journey into the Minification Abyss: The Peculiar '0' Placeholder
JS Bits Bill
JS Bits Bill

Posted on • Edited on

Journey into the Minification Abyss: The Peculiar '0' Placeholder

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
  }
})
Enter fullscreen mode Exit fullscreen mode

With the line __html: (0, $.sanitize)(n.desc), we see 2 curious things:

  1. Why do the parens in (0, $.sanitize) have two expressions?
  2. 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
Enter fullscreen mode Exit fullscreen mode

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.
*/
Enter fullscreen mode Exit fullscreen 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)

Collapse
 
j4k0xb profile image
j4k0xb

This minute difference allows the transpiler to maintain syntactic coherence in it's rules for transformation which facilitate optimization and compatibility
It's just a placeholder to maintaining syntactic harmony

what? 🤔

The actual reason is to ensure that the receiver/this is undefined when calling. Maybe you meant that but I didnt really see it explained in the article...

// source:
import { sanitize } from 'lib';
sanitize(n.desc) // `this` inside sanitize is undefined

// correct output:
const $ = __webpack_require__(1);
(0, $.sanitize)(n.desc); // `this` inside sanitize is undefined

// wrong output:
const $ = __webpack_require__(1);
$.sanitize(n.desc); // `this` inside sanitize is $ (error?)
Enter fullscreen mode Exit fullscreen mode

Not all functions access this but to avoid analyzing (slow) it gets applied anyways

Real example where transpiling to obj.fn(args) instead of (0, obj.fn)(args) resulted in an error: github.com/es-shims/Promise.allSet...

Collapse
 
js_bits_bill profile image
JS Bits Bill

@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.

Collapse
 
miketalbot profile image
Mike Talbot ⭐

Fascinating!!