在 ES7 中,Javascript 新增了 Array.prototype [Symbol.species] 属性,该属性适用于 Array 实例中的函数,如 map()、filter()、slice() 等等。该属性表示在该函数返回一个新的 Array 实例时,该实例应该是来自指定的构造函数,而不是该数组实例自身的构造函数。本篇文章将带你详细了解该属性的用法和意义。
为什么需要 Symbol.species 属性?
在旧版本的 Javascript 中,例如 ES5,Array 实例中的函数通常将返回一个新的 Array 实例。该新实例的构造函数通常来自于该实例自身的构造函数。这种行为可能会导致问题,例如 Array 派生类中重写数组方法时无法保证返回的一定是该派生类的实例,而可能是其它实例。这样一来,就会造成程序行为出现不可预料的变化。
ES7 引入了 Symbol.species 属性来解决这个问题。该属性允许我们指定一个构造函数,而不是在每个派生类中硬编码构造函数的名称。这样,派生类的所有实现都可以引用同一个构造函数,从而避免可能的行为问题。
Symbol.species 的用法
Symbol.species 是一个符号类型的属性,它是一种特殊的、只在定义和实现中使用的属性。它的访问方式与常规属性不同,需要使用 Symbol.species 直接访问。
Symbol.species 属性通常作为类的静态属性,用于定义派生类中操作数组方法时应该返回的实例类型。例如:
class MyArray extends Array { static get [Symbol.species]() { return Array; } } const a = new MyArray(1, 2, 3); const b = a.map(x => x ** 2); console.log(b); // 输出 [1, 4, 9] console.log(b instanceof MyArray); // 输出 false
在以上示例中,我们定义了 MyArray 类,并重写了 map() 方法。在 get Symbol.species 方法中,我们定义了要在该类中使用的构造函数,也就是普通数组 Array。这意味着无论在 MyArray 中调用 map() 方法时都会产生一个新的 Array 实例,而不是 MyArray 的实例。
可以注意到,当我们检查 b 是否是 MyArray 的一个实例时,得到的结果是 false。这是因为在调用 map() 方法时,b 的构造函数来自于 Array.prototype.map,而不是 MyArray。因此,由于 b 不是 MyArray 的一个实例,它不会继承来自于 MyArray 的任何行为。
现在,假设我们将 get Symbol.species 方法定义为 MyArray,如下所示:
class MyArray extends Array { static get [Symbol.species]() { return MyArray; } } const a = new MyArray(1, 2, 3); const b = a.map(x => x ** 2); console.log(b); // 输出 [1, 4, 9] console.log(b instanceof MyArray); // 输出 true
现在,我们重写了 get Symbol.species 方法,将构造函数指定为 MyArray。这意味着在调用 map() 方法时,b 的构造函数来自于 MyArray.prototype.map。因此,b 仍然属于 MyArray 的一个实例,继承了来自 MyArray 的所有行为。
总结
Symbol.species 属性为 Array 类及其派生类提供了更简单和可靠的替代选项。我们可以使用该属性指定一个自定义构造函数,而不必在每个函数中硬编码该信息。这种方式有助于确保操作数组的函数始终返回正确类型的实例,并允许属性继承自定义行为。在使用派生数组时,特别是在重写操作数组的函数时,Symbol.species 属性非常有用,从而保证代码更加健壮和易于维护。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/6502a7fb95b1f8cacdfe3b8c