I'm on TC39. Would Three.js be interested in "tree-shakable methods" syntax?

Good questions! Regarding how these syntaxes help tree shaking and the backwards compatibility story for each…

Vector3 is currently defined like so (simplified):

export class Vector3 {
  constructor(x,y,z) { this.x=x; ... }
  set(x,y,z) { this.x=x; ... }
}

This-based syntax

To be tree shakable and compatible with a this-based chaining syntax, a new Vector3 module could be introduced that exports a much simpler class with a bunch of “extension methods” like so:

export class Vector3 {
  constructor(x,y,z) { this.x=x; ... }
}

/** @this {Vector3} */
export function set(x,y,z) { this.x=x; ... }

Users of this module would experience more reliable tree shaking because they would import the extension methods directly, and the methods are no longer attached to the prototype.

For backwards compatibility, the original Vector3 module would be changed to attach the “extension methods” back onto the prototype:

import {Vector3, set} from '...';
Object.assign(Vector3.prototype, {set});
export {Vector3};

Anyone using this backward compatibility layer would not get the benefits of better tree shaking.

Arg- and Topic-based syntaxes

To be tree shakable and compatible with arg-based or topic-based chaining, a new Vector3 module could be introduced that exports a much simpler class with a bunch of helper functions like so:

export class Vector3 {
  constructor(x,y,z) { this.x=x; ... }
}

/** @param {Vector3} _this */
export function set(_this,x,y,z) { _this.x=x; ... }

Users of this module would experience better tree shaking because they would import the helper functions directly, and methods are no longer attached to the prototype.

For backwards compatibility, the original Vector3 module would be changed to add methods onto the prototype that delegate to the helpers, passing this as needed:

import {Vector3, set} from '...';
Object.assign(Vector3.prototype, {
  set(...args) { return set(this, ...args); },
});
export {Vector3};

Anyone using this backward compatibility module would not get the benefits of better tree shaking.

Technically the backwards compatibility layer here introduces one new function allocation per method, has an extra layer of function call at runtime, and is slightly more verbose compared to the this based strategy which can just re use the same function directly. I assume these costs are trivial in the context of a Threejs program, but correct me if I’m wrong there…

1 Like