子类化 Javascript 数组。类型错误:Array.prototype.toString 不是通用的
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3261587/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Subclassing Javascript Arrays. TypeError: Array.prototype.toString is not generic
提问by Tauren
Is it possible to subclass and inherit from javascript Arrays?
是否可以从 javascript 数组子类化和继承?
I'd like to have my own custom Array object that has all the features of an Array, but contains additional properties. I'd use myobj instanceof CustomArrayto perform specific operations if the instance is my CustomArray.
我想拥有自己的自定义 Array 对象,该对象具有 Array 的所有功能,但包含其他属性。myobj instanceof CustomArray如果实例是我的 CustomArray,我会用来执行特定操作。
After attempting to subclass and running into some problems, I found this Dean Edwardsarticle that indicates doing this with Array objects doesn't work right. It turns out Internet Explorer doesn't handle it properly. But I'm finding other issues as well (only tested in Chrome so far).
在尝试创建子类并遇到一些问题后,我发现了Dean Edwards 的这篇文章,该文章指出对 Array 对象执行此操作是行不通的。事实证明 Internet Explorer 不能正确处理它。但我也发现了其他问题(目前仅在 Chrome 中测试过)。
Here's some sample code:
这是一些示例代码:
/**
* Inherit the prototype methods from one constructor into another
* Borrowed from Google Closure Library
*/
function inherits(childCtor, parentCtor) {
function tempCtor() {};
tempCtor.prototype = parentCtor.prototype;
childCtor.superClass_ = parentCtor.prototype;
childCtor.prototype = new tempCtor();
childCtor.prototype.constructor = childCtor;
},
// Custom class that extends Array class
function CustomArray() {
Array.apply(this, arguments);
}
inherits(CustomArray,Array);
array = new Array(1,2,3);
custom = new CustomArray(1,2,3);
Entering the following in Chrome's console gives this output:
在 Chrome 的控制台中输入以下内容会得到以下输出:
> custom
[]
> array
[1, 2, 3]
> custom.toString()
TypeError: Array.prototype.toString is not generic
> array.toString()
"1,2,3"
> custom.slice(1)
[]
> array.slice(1)
[2, 3]
> custom.push(1)
1
> custom.toString()
TypeError: Array.prototype.toString is not generic
> custom
[1]
Obviously, the objects don't behave the same. Should I give up on this approach, or is there some way to accomplish my goal of myobj instanceof CustomArray?
显然,对象的行为不同。我应该放弃这种方法,还是有什么方法可以实现我的目标myobj instanceof CustomArray?
采纳答案by CMS
Juriy Zaytsev (@kangax) just today released a really good article on the subject.
Juriy Zaytsev ( @kangax) 今天刚刚发布了一篇关于这个主题的非常好的文章。
He explores various alternatives like the Dean Edwards iframe borrowingtechnique, direct object extension, prototype extension and the usage of ECMAScript 5 accessor properties.
他探索了各种替代方案,例如 Dean Edwards iframe 借用技术、直接对象扩展、原型扩展和 ECMAScript 5 访问器属性的使用。
At the end there is no perfect implementation, each one has its own benefits and drawbacks.
最后没有完美的实现,每个实现都有自己的优点和缺点。
Definitely a really good read:
绝对是一本非常好的读物:
回答by laggingreflex
ES6
ES6
class SubArray extends Array {
last() {
return this[this.length - 1];
}
}
var sub = new SubArray(1, 2, 3);
sub // [1, 2, 3]
sub instanceof SubArray; // true
sub instanceof Array; // true
Original Answer: (Not recommended, may cause performance issues)
原答案:(不推荐,可能会导致性能问题)
Copy-pasting from articlementioned in the accepted answer for more visibility
从已接受的答案中提到的文章中复制粘贴以获得更多可见性
Using __proto__
使用 __proto__
function SubArray() {
var arr = [ ];
arr.push.apply(arr, arguments);
arr.__proto__ = SubArray.prototype;
return arr;
}
SubArray.prototype = new Array;
Now you can add your methods to SubArray
现在您可以将您的方法添加到 SubArray
SubArray.prototype.last = function() {
return this[this.length - 1];
};
Initialize like normal Arrays
像普通数组一样初始化
var sub = new SubArray(1, 2, 3);
Behaves like normal Arrays
表现得像普通数组
sub instanceof SubArray; // true
sub instanceof Array; // true
回答by Derek Ziemba
Here's a full example that should work on ie9 and greater. For <=ie8 you'd have to implement alternatives to Array.from, Array.isArray, etc. This example:
这是一个应该在 ie9 及更高版本上工作的完整示例。对于 <=ie8,您必须实现 Array.from、Array.isArray 等的替代方案。此示例:
- Puts the Array subclass in its own closure (or Namespace) to avoid conflicts and namespace pollution.
- Inherits all prototypes and properties from the native Array class.
- Shows how to define additional properties and prototype methods.
- 将 Array 子类放在它自己的闭包(或命名空间)中以避免冲突和命名空间污染。
- 从本机 Array 类继承所有原型和属性。
- 展示如何定义额外的属性和原型方法。
If you can use ES6, you should use the class SubArray extends Arraymethod laggingreflex posted.
如果你可以使用 ES6,你应该使用class SubArray extends Arraylaggingreflex 发布的方法。
Here is the essentials to subclass and inherit from Arrays. Below this excerpt is the full example.
这是从 Arrays 子类化和继承的基本要素。在此摘录下方是完整示例。
///Collections functions as a namespace.
///_NativeArray to prevent naming conflicts. All references to Array in this closure are to the Array function declared inside.
var Collections = (function (_NativeArray) {
//__proto__ is deprecated but Object.xxxPrototypeOf isn't as widely supported. '
var setProtoOf = (Object.setPrototypeOf || function (ob, proto) { ob.__proto__ = proto; return ob; });
var getProtoOf = (Object.getPrototypeOf || function (ob) { return ob.__proto__; });
function Array() {
var arr = new (Function.prototype.bind.apply(_NativeArray, [null].concat([].slice.call(arguments))))();
setProtoOf(arr, getProtoOf(this));
return arr;
}
Array.prototype = Object.create(_NativeArray.prototype, { constructor: { value: Array } });
Array.from = _NativeArray.from;
Array.of = _NativeArray.of;
Array.isArray = _NativeArray.isArray;
return { //Methods to expose externally.
Array: Array
};
})(Array);
Full example:
完整示例:
///Collections functions as a namespace.
///_NativeArray to prevent naming conflicts. All references to Array in this closure are to the Array function declared inside.
var Collections = (function (_NativeArray) {
//__proto__ is deprecated but Object.xxxPrototypeOf isn't as widely supported. '
var setProtoOf = (Object.setPrototypeOf || function (ob, proto) { ob.__proto__ = proto; return ob; });
var getProtoOf = (Object.getPrototypeOf || function (ob) { return ob.__proto__; });
function Array() {
var arr = new (Function.prototype.bind.apply(_NativeArray, [null].concat([].slice.call(arguments))))();
setProtoOf(arr, getProtoOf(this));//For any prototypes defined on this subclass such as 'last'
return arr;
}
//Restores inherited prototypes of 'arr' that were wiped out by 'setProtoOf(arr, getProtoOf(this))' as well as add static functions.
Array.prototype = Object.create(_NativeArray.prototype, { constructor: { value: Array } });
Array.from = _NativeArray.from;
Array.of = _NativeArray.of;
Array.isArray = _NativeArray.isArray;
//Add some convenient properties.
Object.defineProperty(Array.prototype, "count", { get: function () { return this.length - 1; } });
Object.defineProperty(Array.prototype, "last", { get: function () { return this[this.count]; }, set: function (value) { return this[this.count] = value; } });
//Add some convenient Methods.
Array.prototype.insert = function (idx) {
this.splice.apply(this, [idx, 0].concat(Array.prototype.slice.call(arguments, 1)));
return this;
};
Array.prototype.insertArr = function (idx) {
idx = Math.min(idx, this.length);
arguments.length > 1 && this.splice.apply(this, [idx, 0].concat([].pop.call(arguments))) && this.insert.apply(this, arguments);
return this;
};
Array.prototype.removeAt = function (idx) {
var args = Array.from(arguments);
for (var i = 0; i < args.length; i++) { this.splice(+args[i], 1); }
return this;
};
Array.prototype.remove = function (items) {
var args = Array.from(arguments);
for (var i = 0; i < args.length; i++) {
var idx = this.indexOf(args[i]);
while (idx !== -1) {
this.splice(idx, 1);
idx = this.indexOf(args[i]);
}
}
return this;
};
return { //Methods to expose externally.
Array: Array
};
})(Array);
Here are some usage examples and tests.
以下是一些使用示例和测试。
var colarr = new Collections.Array("foo", "bar", "baz", "lorem", "ipsum", "lol", "cat");
var colfrom = Collections.Array.from(colarr.reverse().concat(["yo", "bro", "dog", "rofl", "heyyyy", "pepe"]));
var colmoded = Collections.Array.from(colfrom).insertArr(0, ["tryin", "it", "out"]).insert(0, "Just").insert(4, "seems", 2, "work.").remove('cat','baz','ipsum','lorem','bar','foo');
colmoded; //["Just", "tryin", "it", "out", "seems", 2, "work.", "lol", "yo", "bro", "dog", "rofl", "heyyyy", "pepe"]
colmoded instanceof Array; //true
回答by termi
Checkout this. It works as it should in all browsers which support '__proto__'.
结帐这个。它在所有支持“ __proto__”的浏览器中都能正常工作。
var getPrototypeOf = Object.getPrototypeOf || function(o){
return o.__proto__;
};
var setPrototypeOf = Object.setPrototypeOf || function(o, p){
o.__proto__ = p;
return o;
};
var CustomArray = function CustomArray() {
var array;
var isNew = this instanceof CustomArray;
var proto = isNew ? getPrototypeOf(this) : CustomArray.prototype;
switch ( arguments.length ) {
case 0: array = []; break;
case 1: array = isNew ? new Array(arguments[0]) : Array(arguments[0]); break;
case 2: array = [arguments[0], arguments[1]]; break;
case 3: array = [arguments[0], arguments[1], arguments[2]]; break;
default: array = new (Array.bind.apply(Array, [null].concat([].slice.call(arguments))));
}
return setPrototypeOf(array, proto);
};
CustomArray.prototype = Object.create(Array.prototype, { constructor: { value: CustomArray } });
CustomArray.prototype.append = function(var_args) {
var_args = this.concat.apply([], arguments);
this.push.apply(this, var_args);
return this;
};
CustomArray.prototype.prepend = function(var_args) {
var_args = this.concat.apply([], arguments);
this.unshift.apply(this, var_args);
return this;
};
["concat", "reverse", "slice", "splice", "sort", "filter", "map"].forEach(function(name) {
var _Array_func = this[name];
CustomArray.prototype[name] = function() {
var result = _Array_func.apply(this, arguments);
return setPrototypeOf(result, getPrototypeOf(this));
}
}, Array.prototype);
var array = new CustomArray(1, 2, 3);
console.log(array.length, array[2]);//3, 3
array.length = 2;
console.log(array.length, array[2]);//2, undefined
array[9] = 'qwe';
console.log(array.length, array[9]);//10, 'qwe'
console.log(array+"", array instanceof Array, array instanceof CustomArray);//'1,2,,,,,,,,qwe', true, true
array.append(4);
console.log(array.join(""), array.length);//'12qwe4', 11
回答by Matchu
I've tried to do this sort of thing before; generally, it just doesn't happen. You can probably fake it, though, by applying Array.prototypemethods internally. This CustomArrayclass, though only tested in Chrome, implements both the standard pushand custom method last. (Somehow this methodology never actually occurred to me at the time xD)
我以前尝试过做这种事情;一般来说,它不会发生。但是,您可以通过在Array.prototype内部应用方法来伪造它。这个CustomArray类虽然只在 Chrome 中测试过,但实现了标准push和自定义方法last。(不知何故,我当时从未真正想到过这种方法 xD)
function CustomArray() {
this.push = function () {
Array.prototype.push.apply(this, arguments);
}
this.last = function () {
return this[this.length - 1];
}
this.push.apply(this, arguments); // implement "new CustomArray(1,2,3)"
}
a = new CustomArray(1,2,3);
alert(a.last()); // 3
a.push(4);
alert(a.last()); // 4
Any Array method you intended to pull into your custom implementation would have to be implemented manually, though you could probably just be clever and use loops, since what happens inside our custom pushis pretty generic.
您打算引入自定义实现的任何 Array 方法都必须手动实现,尽管您可能只是聪明点并使用循环,因为在我们的自定义中发生的事情push非常通用。
回答by Amit Portnoy
I've created a simple NPM module that solves this - inherit-array. It basically does the following:
我创建了一个简单的 NPM 模块来解决这个问题 - inherit-array。它主要执行以下操作:
function toArraySubClassFactory(ArraySubClass) {
ArraySubClass.prototype = Object.assign(Object.create(Array.prototype),
ArraySubClass.prototype);
return function () {
var arr = [ ];
arr.__proto__ = ArraySubClass.prototype;
ArraySubClass.apply(arr, arguments);
return arr;
};
};
After writing your own SubArrayclass you can make it inherit Array as follows:
编写自己的SubArray类后,您可以使其继承 Array,如下所示:
var SubArrayFactory = toArraySubClassFactory(SubArray);
var mySubArrayInstance = SubArrayFactory(/*whatever SubArray constructor takes*/)

