The MDN gives the following working example of Symbol.species
:
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
The ECMAScript 2015 specifications says:
A function valued property that is the constructor function that is used to create derived objects.
The way I understand it it would be mainly used to Duck-Type in some way custom objects in order to trick the instanceof
operator.
It seems very useful but I have not managed to use it on plain objects (FF 44.0a2):
let obj = {
get [Symbol.species]() {
return Array
}
}
console.log(obj instanceof Array) //false =(
console.log(obj instanceof Object) //true
Is there any way to use Symbol.species
on plain objects in order to trick the instanceof
operator?
The way I understand it it would be mainly used to Duck-Type in some way custom objects in order to trick the
instanceof
operator.
Nope, that's what Symbol.hasInstance
is good for (though it makes tricky constructors, e.g. for mixins, not tricky instances).
The point of Symbol.species
is to let built-in methods determine the proper type for derived objects. Whenever a function is supposed to return a new instance of the same kind, it usually instantiates a new this.constructor
, which might be a subclass of the class that defined the method. But you might not always want this when subclassing, and that's where Symbol.species
comes in.
The example given by MDN is quite good, you just must not miss the distinction between a
and mapped
:
var a = new MyArray(1,2,3);
var mapped = a.map(x => x * x);
Object.getPrototypeOf(a) == MyArray.prototype; // true
Object.getPrototypeOf(mapped) == Array.prototype; // true
(As you know, instanceof
is was just sugar for the reverse of isPrototypeOf
)
So what happens here is that the Array map
method is called on an instance of MyArray
, and creates a derived object that is now an instance of Array
- because a.constructor[Symbol.species]
said so. Without it, map
would have created another MyArray
.
This trick would work with plain objects just as well:
var b = {
length: 0,
map: Array.prototype.map,
constructor: { // yes, usually a function
[Symbol.species]: MyArray
}
};
var mapped = b.map(x => x*x);
console.log(mapped instanceof MyArray); // true
I can't say whether it's any useful in this example, though :-)
Good question. I admit that I'm not 100% sure, but I've gotten the impression that the answer is no.
I used two methods to determine this answer:
The first point wasn't easy to test, and I wasn't entirely sure that I successfully did it. You used Firefox 44, but I decided to test another environment due to the fact that Kangax's support table says it only supports the existence of Symbol.species, and none of the Array-like features. To see this, you can expand the Symbol.species section in the link above.
In this case, even Kangax is a bit confusing. I would expect there to be non-Array specific tests for Symbol.species aside from the existence test, but maybe I'm just not knowledge enough to understand that those tests cover all of the functionality of Symbols. I'm not sure!
While browsing the table, I saw that Edge supports all of the Symbol.species features. I loaded up my Windows VM, hopped on over to Edge 20, and pasted your code.
In Edge 20, the code had the same result as your test in Firefox 44.
So this began to give me confidence that the feature doesn't work in the way that you want it to. Just to make sure that Edge had its JavaScript in order, I decided to run the code from the MDN article you linked, and...I got a syntax error! It appears Edge is still updating its Class implementation due to late-in-the-game changes to the Class spec, so it's not enabled.
Their current implementation is, however, available through an experimental flag. So I flipped that on and was able to reproduce the result that MDN got.
And that was the extent of my JavaScript environment testing. I don't have 100% confidence in this test, but I would say that I'm fairly confidence in the result.
So onto the second method I used to determine my result: the specification.
I admit that I'm no expert at the ECMAScript specification. I do my best to understand it, but it is likely that I might misinterpret some of the information in there. The reason for this is because I'm not an ECMAScript master, so some of the technical jargon used is just way over my head.
Nevertheless, I can sometimes get the gist of what is being said. Here's a snippet that's about the Symbol @@species:
Methods that create derived collection objects should call @@species to determine the constructor to use to create the derived objects. Subclass constructor may over-ride @@species to change the default constructor assignment.
The phrase "derived objects" is used quite frequently in describing this symbol, but it's not defined anywhere. One might interpret it to be a synonym of sorts with instantiated. If that's a correct interpretation, then this can likely only be used with constructors or classes.
Either way, it sounds as if @@species is an active Symbol, rather than a passive one. By this I mean that it sounds like it's a function that's actually called during the creation of an Object, which is then what determines the instanceof
value, rather than a function that's called at the time of instanceof
being called.
But anyway, take that interpretation with a grain of salt. Once again, I'm not 100% certain of any of this. I considered posting this as a comment rather than an answer, but it ended up getting quite long.
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