使用 setPrototypeOf 进行数组子类化 [英] Array subclassing with setPrototypeOf

查看:24
本文介绍了使用 setPrototypeOf 进行数组子类化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以我阅读了一些关于 在 JavaScript 中子类化 Array.关于该主题的一般观点是,无法创建具有某种缺点的子类.

So I read some blog posts, SO threads and other lectures about subclassing Array in JavaScript. The general view on the topic is that there is no way to create a subclass with some kind of downside.

在尝试一些事情时,我为自己想出了这个解决方案:

While trying out a few things I came up with this solution for myself:

// This is the constructor of the new class.
function CustomArray() {

    // The "use strict" statement changes the way the "magic" variable
    // "arguments" works and makes code generally safer.
    "use strict";

    // Create an actual array. This way the object will treat numeric
    // properties in the special way it is supposed to.
    var arr = [],
        i;

    // Overwrite the object's prototype, so that CustomArray.prototype is
    // in the prototype chain and the object inherits its methods. This does
    // not break the special behaviour of arrays.
    Object.setPrototypeOf(arr, CustomArray.prototype);

    // Take all arguments and push them to the array.
    for (i = 0; i < arguments.length; i++) {
        arr.push(arguments[i]);
    }

    // Return the array with the modified prototype chain. This overwrites
    // the return value of the constructor so that CustomArray() really
    // returns the modified array and not "this".
    return arr;
}

// Make CustomArray inherit from Array.
CustomArray.prototype = Object.create(Array.prototype);

// Define a method for the CustomArray class.
CustomArray.prototype.last = function () {
    return this[this.length - 1];
};

var myArray = new CustomArray("A", "B", 3);
// [ "A", "B", 3 ]

myArray.length;
// 3

myArray.push("C");
// [ "A", "B", 3, "C" ]

myArray.length;
// 4

myArray.last();
// "C"

我的问题是:这段代码有什么问题吗?我很难相信在我之前有这么多人搜索后,我想出了唯一的解决方案".

My question is: Is there anything wrong with this code? I find it hard to believe that I came up with "the one solution" after so many people searched before me.

推荐答案

文章讨论了如何创建数组子类".也就是说,我们想要创建一个在其原型链中具有 Array.prototype 的对象,但具有一个不是 Array.prototype 的直接原型父对象(即,这样原型父级可以提供数组原型之外的其他方法).

The article discusses how to create an array "subclass". That is, we want to create an object that has Array.prototype in its prototype chain, but with an immediate prototype parent that is not Array.prototype (i.e., so that the prototype parent can provide additional methods beyond the array prototype).

那篇文章说,创建数组子类"的一个基本困难是数组的行为来自两者

That article says that a fundamental difficulty in creating an array "subclass" is that arrays get their behavior from both

  1. 他们的原型,
  2. 只是数组实例.

如果数组从 Array.prototype 继承了它们的所有行为,我们的工作会非常快.我们将简单地创建一个对象,其原型链包含 Array.prototype.该对象将成为我们的数组子类实例的理想原型.

If arrays inherited all their behavior from Array.prototype, our work would be very quick. We would simply create an object whose prototype chain includes Array.prototype. That object would make an ideal prototype for our array-subclass instances.

然而,数组具有数组实例独有的特殊自动行为,并且不是从原型继承的.(特别是,我的意思是 length 属性周围的行为会在数组更改时自动更改,反之亦然.)这些是由 Array<创建时提供给每个数组实例的行为/code> 构造函数,在 ECMAScript 5 中没有办法忠实地模仿它们.因此,您的数组子类的实例必须最初由 Array 构造函数创建.如果我们想要适当的 length 行为,这是不可协商的.

However, arrays have special automatic behaviors that are unique to array instances, and are not inherited from the prototype. (In particular, I mean the behaviors around the length property automatically changing when the array changes, and vice versa.) These are behaviors supplied to each array instance when it is created by the Array constructor, and there is no way to mimic them faithfully in ECMAScript 5. Therefore, the instance of your array subclass must be originally created by the Array constructor. This is non-negotiable, if we want the appropriate length behaviors.

此要求与我们的其他要求相冲突,即实例必须具有不是 Array.prototype 的原型.(我们不想向 Array.prototype 添加方法;我们想向使用 Array.prototype 作为其自身原型的对象添加方法.)在 ECMAScript 5 中,使用 Array 构造函数创建的任何对象都必须具有 Array.prototype 的原型父级.ECMAScript 5 规范没有提供在创建对象后更改其原型的机制.

This requirement conflicts with our other requirement that the instance must have a prototype that is not Array.prototype. (We don't want to add methods to Array.prototype; we want to add methods to an object that uses Array.prototype as its own prototype .) In ECMAScript 5, any object created using the Array constructor must have the prototype parent of Array.prototype. The ECMAScript 5 spec provides no mechanism to change an object's prototype after it is created.

相比之下,ECMAScript 6 确实提供了这样的机制.您的方法与文章中描述的基于 __proto__ 的方法非常相似,位于Wrappers.原型链注入.,除非您使用 ECMAScript 6 的 Object.setPrototypeOf 而不是 __proto__.

By contrast, ECMAScript 6 does provide such a mechanism. Your approach is quite similar to the __proto__-based approach described in the article, under the section "Wrappers. Prototype chain injection.," except you use ECMAScript 6's Object.setPrototypeOf instead of __proto__.

您的解决方案正确满足以下所有要求:

Your solution correctly satisfies all of the following requirements:

  1. 每个实例实际上都是一个数组(即,已经由 Array 构造函数构造).这可确保 [[Class]] 内部属性正确,并且 length 行为正确.
  2. 每个实例都有一个直接原型,它不是 Array.prototype,但在其原型链中仍包含 Array.prototype.
  1. Each instance actually is an array (i.e., has been constructed by the Array constructor). This ensures that the [[Class]] internal property is correct, and length behaves correctly.
  2. Each instance has an immediate prototype that is not Array.prototype, but still includes Array.prototype in its prototype chain.

这些要求以前在 ES5 中是不可能满足的,但 ES6 使它变得相当容易.在 ES5 中,您可能有一个无法满足需求 #2 的数组实例,或者一个无法满足需求 #1 的普通对象.

These requirements were previously impossible to satisfy in ES5, but ES6 makes it fairly easy. In ES5, you could have an array instance that fails to satisfy requirement #2, or a plain object that fails to satisfy requirement #1.

这篇关于使用 setPrototypeOf 进行数组子类化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆