Skip to content

Instantly share code, notes, and snippets.

@littledan
Last active November 14, 2015 00:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save littledan/ca5dd9a0d1f0910a1d7c to your computer and use it in GitHub Desktop.
Save littledan/ca5dd9a0d1f0910a1d7c to your computer and use it in GitHub Desktop.

Reconsidering @@species

ES2015 added the @@species feature, where subclassing of builtins is enhanced by creating instances not of this.constructor but of this.constructor[Symbol.species]. There are a few issues with @@species:

  • @@species is called in some places but not others in a way that feels a bit inconsistent (from instance methods but not static methods). See bugs 1 2 (Kevin Smith)
  • @@species feels a bit unnecessary. It was motivated initially by ensuring Zepto's strange use of proto would continue to work. There's already an extension point by overwriting the constructor field, a simpler fix would have been possible, and Zepto was updated a year ago to not use these patterns.
  • @@species will be a lot of work to implement across standard libraries of all JS engines without a performance regression.

This document explores whether we can remove the @@species feature and just build instances from new this.constructor() instead.

Zepto compatibility concerns

@@species came up on TC39's agenda in an attempt to preserve compatibility with Zepto. A patch was merged on October 21, 2014 to remove the problematic use of proto, but the old code can be found here. Here's the relevant snippet:

  zepto.Z = function(dom, selector) {
    dom = dom || []
    dom.__proto__ = $.fn
    dom.selector = selector || ''
    return dom
  }

In this old version, $.fn is an object literal with no constructor property. Using @@species, callers to Zepto could make a quick fix by setting Object[Symbol.species] = Array so that the Array methods that are copied onto $.fn continue to work (NB: or does it not need any intervention?).

Alternative Zepto fix

However, a simpler fix is possible: ArraySpeciesCreate could check for a constructor which is Object and fall back to ArrayCreate(0) in that case. The spec text would be, in ArraySpeciesCreate, 5.c:

5.c. If Type(C) is Object

i. If Samevalue(C, %Object%), let C be undefined

Basically all other uses of @@species would just be removed, using the constructor as the species.

Implementation difficulties of @@species

@@species adds extension points all over the place. Many callsites which had a simpler way of finding the constructor (either this.constructor or a fixed constructor) now have an extra Get in the path, which could invoke arbitrary code. In some implementations, extra checks will have to be done at runtime to be able to run fastpaths where @@species remains the default. The standard library has to be updated in many different places.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment