Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ES6 in ES6 #43

Open
Merri opened this issue Oct 19, 2019 · 1 comment
Open

ES6 in ES6 #43

Merri opened this issue Oct 19, 2019 · 1 comment

Comments

@Merri
Copy link

Merri commented Oct 19, 2019

I know it might be annoying to have two sets of "same code", but modern JavaScript engines optimize ES6 code which means it is possible to reduce uglified JS size by about 400 bytes without sacrificing any performance (except on legacy JS engines).

So here is a version based on ES6 from beta, but with optimizations that reduce code size. On my machine this seems to give more ops/sec than the current beta ES6 version, although the difference is pretty small. Also I guess the benchmark does not yet include anything to test perf of ES6 features.

Optimizations

  1. BigInt arrays have one less boolean check on each iteration.
  2. constructor is stored in local variable and all calls using it are immediately after.
  3. Arrays and Typed arrays use the same path (with different comparison).
  4. Maps and Sets use the same path (Map has one extra comparison).
  5. Using .every() extensively to reduce code size.

Optimizations that use combined path will of course have slightly worse performance than if they were written separate (due to extra boolean storage & check per path). However the reduction in ops/sec is so tiny that I'd prefer shorter code due to slightly faster download and initial parsing on browser.

const typed = [
  Int8Array,
  Uint8Array,
  Uint8ClampedArray,
  Int16Array,
  Uint16Array,
  Int32Array,
  Uint32Array,
  Float32Array,
  Float64Array
];

if (typeof BigInt64Array !== 'undefined')
  typed.push(BigInt64Array, BigUint64Array);

module.exports = function equal(a, b) {
  if (a === b)
    return true;

  if (!a || !b || typeof a !== 'object' || typeof b !== 'object')
    // true if both NaN, false otherwise
    return a !== a && b !== b;

  const constructor = a.constructor;

  if (constructor !== b.constructor)
    return false;

  if (constructor === RegExp)
    return a.source === b.source && a.flags === b.flags;

  const isArray = Array.isArray(a);
  // .some(...) -> .includes(constructor) would be shorted, but not sure if that works correctly
  if (isArray || (constructor.BYTES_PER_ELEMENT && typed.some(type => a instanceof type))) {
    let i = a.length;

    if (i === b.length) {
      if (isArray)
        while (i-- > 0 && equal(a[i], b[i]));
      else
        while (i-- > 0 && a[i] === b[i]);
    }

    return i === -1;
  }

  const isMap = a instanceof Map;

  if (isMap || a instanceof Set) {
    if (a.size !== b.size)
      return false;

    if (!a.keys().every(b.has))
      return false;

    if (isMap && !a.keys().every(key => equal(a.get(key), b.get(key))))
      return false;

    return true;
  }

  if (a.valueOf !== Object.prototype.valueOf)
    return a.valueOf() === b.valueOf();

  if (a.toString !== Object.prototype.toString)
    return a.toString() === b.toString();

  const keys = Object.keys(a);

  if (keys.length !== Object.keys(b).length)
    return false;

  if (!keys.every(Object.prototype.hasOwnProperty.bind(b)))
    return false;

  return keys.every(key => equal(a[key], b[key]));
}

Uglified size: 1075 bytes.
Current ES6 beta uglified: 1473 bytes.

Edit: Although gzipped size difference is about ten bytes, so actual download size is about the same thus making the point of smaller download size near invalid :)

@darcyparker
Copy link

Did you test this with Map and Set?
!a.keys().every(b.has) seems flawed. keys() returns an IterableIterator. It does not have .every().

typeof (new Map()).keys().every === 'undefined'

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants