I was just translating an article about Symbol.species in MDN, when I stumpled upon the following section:
You might want to return Array objects in your derived array class MyArray. For example, when using methods such as map() that return the default constructor, you want these methods to return a parent Array object, instead of the MyArray object. The species symbol lets you do this:
class MyArray extends Array { // Overwrite species to the parent Array constructor static get [Symbol.species]() { return Array; } } var a = new MyArray(1,2,3); var mapped = a.map(x => x * x); console.log(mapped instanceof MyArray); // false console.log(mapped instanceof Array); // true
Beside having trouble to translate the text, which didn't give me the feeling to know what's going on, the code doesn't prove their point. When I comment out the line static get [Symbol.species]() { return Array; }
the result is exactly the same.
Here's my code:
"use strict"; class MyArray extends Array { // Overwrite species to the parent Array constructor // static get [Symbol.species]() { return Array; } } var a = new MyArray(1,2,3); var mapped = a.map(x => x * x); console.log(mapped instanceof MyArray); // false console.log(mapped instanceof Array); // true
And my console output:
➜ node test.js false true
Is that article simply wrong, or am I, as a JS/Node beginner, missing an important detail?
The well-known symbol Symbol. species specifies a function-valued property that the constructor function uses to create derived objects.
Symbols are new primitive type introduced in ES6. Symbols are completely unique identifiers. Just like their primitive counterparts (Number, String, Boolean), they can be created using the factory function Symbol() which returns a Symbol. Every time you call the factory function, a new and unique symbol is created.
JavaScript symbol You can use that symbol (the random big number) as a key in objects. A symbol is created by calling the Symbol function which takes an optional argument string which is used only for debugging purposes and acts as a description of the symbol. The Symbol function returns a unique symbol value.
You are missing an important detail. If you subclass array, and then do a map
, using Symbol.species you can get back an array, not a member of your derived class. Sometimes you want that, for instance if you are exposing an API as a library author. You may want some special subclass sauce for your internal use but expose methods that return regular arrays for public consumption.
And there's no real limit, you can set up an alternate constructor for anything at all.
As for why commenting out the line doesn't change anything, remember that subclassing built-ins is new, may not be fully and correctly implemented, and that this is especially true with node.js where if they jump on the bandwagon early they get stuck supporting something 'wrong' for years in an LTS (they got burned on that on Object.observe).
When I comment out the line
static get [Symbol.species]() { return Array; }
the result is exactly the same.
In a correct implementation of ES6 it wouldn't be the same!
For anyone looking for more examples, this article from Keith Cirkel (keithamus on Github) does a lot more at exlaining the concept than the MDN docs.
And here is an excerpt (emphasis mine):
"Now, if you were to make a
class Foo extends Array
- every time you calledFoo#map
while before it would return an Array (no fun) and you’d have to write your own Map implementation just to createFoo
s instead ofArray
s, nowFoo#map
return aFoo
, thanks toSymbol.species
"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With