所以我读了一些关于subclassing Array
in JavaScript的博客文章,SO主题和其他讲座。关于该主题的一般观点是,没有办法创建具有某种缺点的子类。
在尝试一些事情时,我为自己想出了这个解决方案:
// 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"
我的问题是:这段代码有什么问题吗?在很多人在我面前搜索之后,我发现很难相信我想出了“一个解决方案”。
答案 0 :(得分:2)
本文讨论了如何创建数组“子类”。也就是说,我们想要创建一个在其原型链中具有Array.prototype
的对象,但是具有不是Array.prototype
的直接原型父对象(即,原型父对象可以提供除数组之外的其他方法)原型)。
那篇文章说创建数组“子类”的一个根本难点是数组从得到它们的行为
如果数组继承了Array.prototype
的所有行为,我们的工作会很快。我们只需创建一个原型链包含Array.prototype
的对象。该对象将成为我们的数组子类实例的理想原型。
但是,数组具有数组实例唯一的特殊自动行为,并且不会从原型继承。 (特别是,我指的是length
属性周围的行为在数组更改时自动更改,反之亦然。)这些是由Array
构造函数创建时提供给每个数组实例的行为,并且没有办法在ECMAScript 5中忠实地模仿它们。因此,数组子类的实例必须最初由Array
构造函数创建。如果我们想要适当的length
行为,这是不可协商的。
此要求与我们的其他要求相冲突,即实例必须具有非Array.prototype
的原型。 (我们不想向Array.prototype
添加方法;我们希望将方法添加到使用Array.prototype
作为其自己的原型的对象。)在ECMAScript 5中,使用{{1}创建的任何对象构造函数必须具有Array
的原型父级。 ECMAScript 5规范没有提供在创建对象原型后更改对象原型的机制。
相比之下,ECMAScript 6确实提供了这样一种机制。除了使用ECMAScript 6的Array.prototype
而不是__proto__
之外,您的方法与本文“Wrappers. Prototype chain injection.”部分中描述的基于Object.setPrototypeOf
的方法非常相似。 / p>
您的解决方案正确满足以下所有要求:
__proto__
构造函数构造)。这可确保Array
内部属性正确,[[Class]]
行为正确。length
的直接原型,但在原型链中仍然包含Array.prototype
。以前在ES5中无法满足这些要求,但ES6使其变得相当容易。在ES5中,您可能有一个无法满足要求#2的数组实例,或者无法满足要求#1的普通对象。
答案 1 :(得分:1)
实际上,通过利用神秘的Object.setPrototypeOf()
方法,甚至无需触摸__proto__
或Array.of()
即可进行数组子类化。 Array.of()
能够切换用于构造数组的构造函数。由于它通常绑定到Array
对象,因此它会生成普通数组,但是一旦它绑定到另一个可以用作构造函数(a.k.a函数)的对象,它就会将该对象用作构造函数。让我们用Array.of()
function SubArr(){}
SubArr.prototype = Object.create(Array.prototype);
SubArr.prototype.last = function(){return this[this.length-1]};
var what = Array.of.call(SubArr, 1, 2, 3, 4, "this is last");
console.log(JSON.stringify(what,null,2));
console.log(what.last());
console.log(what.map(e => e));
console.log(what instanceof Array);
console.log(Array.isArray(what));
what.unshift("this is first");
console.log(JSON.stringify(what,null,2));
因此,当您使用Array.of()
完成时,您会看到数组子类化非常简单。您可以找到它的规格here。有趣的部分是;
注2:功能是一种有意的通用工厂方法;它 不要求其值为Array构造函数。 因此,它可以转移到其他构造函数或由其他构造函数继承 可以使用单个数字参数调用。