是否可以将Symbol.species用于普通对象?

时间:2015-11-28 14:17:55

标签: javascript ecmascript-6

MDN提供了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

ECMAScript 2015 specifications说:

  

一个函数值属性,它是用于创建派生对象的构造函数。

我理解它的方式主要用于以某种方式对自定义对象进行Duck-Type以欺骗instanceof运算符。
它似乎非常有用,但我没有设法在普通对象上使用它(FF 44.0a2):

let obj = {
  get [Symbol.species]() {
    return Array
  }
}

console.log(obj instanceof Array)  //false =(
console.log(obj instanceof Object) //true

有没有办法在普通对象上使用Symbol.species来欺骗instanceof运算符?

2 个答案:

答案 0 :(得分:6)

  

我理解它的方式主要用于以某种方式对自定义对象进行Duck-Type以欺骗instanceof运算符。

不,那是Symbol.hasInstance有用的东西(尽管它制作棘手的构造函数,例如mixin,而不是棘手的实例)。

Symbol.species的要点是让内置方法确定派生对象的正确类型。每当函数应该返回同一类型的新实例时,它通常会实例化new this.constructor,它可能是定义该方法的类的子类。但是在子类化时你可能并不总是想要这个,而那里的Symbol.species来自 。 MDN提供的示例非常好,您不能错过amapped之间的区别:

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

(如你所知,instanceof 只是isPrototypeOf的逆转

所以这里发生的是Array map methodMyArray的实例上调用,而creates a derived object现在是Array的实例 - 因为{{1} } 我说过了。没有它,a.constructor[Symbol.species]会创建另一个map

这个技巧也适用于普通对象:

MyArray

我不能说这个例子中是否有用,但是: - )

答案 1 :(得分:2)

好问题。我承认我并非百分百肯定,但我得到的答案是没有

我使用两种方法来确定这个答案:

  1. 找到一个明确支持测试所需的所有功能的环境,并直接在该环境中运行代码
  2. 引用the spec
  3. 第一点不容易测试,我不能完全确定我成功地做到了。您使用的是Firefox 44,但我决定测试另一个环境,因为Kangax的支持表说它only supports the existence of Symbol.species,并且没有类似Array的功能。要查看此内容,您可以展开上面链接中的Symbol.species部分。

    在这种情况下,即使Kangax也有点混乱。我希望除了存在测试之外,还会对Symbol.species进行非数组特定测试,但是我可能还没有足够的知识来理解这些测试涵盖了符号的所有功能。我不确定!

    在浏览表格时,我看到Edge支持所有Symbol.species功能。我加载了我的Windows VM,跳到了Edge 20,然后粘贴了你的代码。

    在Edge 20中,代码与Firefox 44中的测试结果相同

    所以这开始让我相信这个功能并不像你想要的那样工作。为了确保Edge按顺序编写JavaScript,我决定从您链接的MDN文章中运行代码,并且......我遇到语法错误!看来Edge仍在更新其Class实现,因为游戏中的类规范有更改,所以它没有启用。

    然而,他们目前的实施是通过实验旗帜获得的。所以我打开了它,并且能够重现MDN得到的结果。

    这就是我的JavaScript环境测试的范围。我对此测试没有100%的信心,但我会说我对结果非常充满信心。

    所以在我用来确定结果的第二种方法上:规范。

    我承认我不是ECMAScript规范的专家。我尽力理解它,但很可能我可能误解了那里的一些信息。这样做的原因是因为我不是ECMAScript的主人,所以使用的一些技术术语就是我的头脑。

    然而,我有时可以得到所说内容的要点。这是一个关于Symbol @@ species:

    的摘录
      

    创建派生集合对象的方法应调用@@ species来确定用于创建派生对象的构造函数。子类构造函数可能会覆盖@@ species以更改默认构造函数赋值。

    短语"派生对象"在描述此符号时经常使用它,但它没有在任何地方定义。有人可能会将其解释为具有实例化的排序的同义词。如果这是正确的解释,那么这可能只能用于构造函数或类。

    无论哪种方式,听起来好像@@ species是活动符号,而不是被动符号。我的意思是,这听起来像是在创建一个Object期间实际调用的函数,然后是决定instanceof值的函数,而不是一个函数。在被调用的instanceof 时调用

    但无论如何,要用一粒盐来解释。再一次,我并非100%肯定这一点。我认为这是一个评论而不是一个答案,但它最终变得很长。