扩展原生 JavaScript 数组
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14000645/
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
Extend native JavaScript array
提问by BalaKrishnan?
Is there any way to inherit a class from JS native function?
有没有办法从JS本机函数继承一个类?
For example, I have a JS function like this:
例如,我有一个这样的 JS 函数:
function Xarray()
{
Array.apply(this, arguments);
//some stuff for insert, add and remove notification
}
Xarray.prototype = new Array();
I tried to convert it to Typescript but i failed!!
我试图将它转换为 Typescript 但我失败了!!
export class Xarray implements Array {
}
The compiler asks me to define all Arrayinterface properties. I know if I need this Xarray.prototype = new Array();, I have to extend Arrayin TS.
编译器要求我定义所有Array接口属性。我知道如果我需要这个Xarray.prototype = new Array();,我必须Array在 TS 中扩展。
How to extend the JS native object in TS?
如何在TS中扩展JS原生对象?
采纳答案by Rajagopal ?
I don't think there is a way to inherit existing interfaces like Array,
我认为没有办法继承像 Array 这样的现有接口,
export class Xarray implements Array {
}
You should create a function and inherit it with its prototype. Typescript also will accept it which is similar to javascript.
你应该创建一个函数并用它的原型继承它。Typescript 也会接受它,这类似于 javascript。
function Xarray(...args: any[]): void; // required in TS 0.9.5
function Xarray()
{
Array.apply(this, arguments);
// some stuff for insert, add and remove notification
}
Xarray.prototype = new Array();
UPDATE:This one is discussed well and provided the best solution for this at jqfaq.com.
更新:在jqfaq.com 上对此进行了很好的讨论,并为此提供了最佳解决方案。
//a dummy class it to inherite array.
class XArray {
constructor() {
Array.apply(this, arguments);
return new Array();
}
// we need this, or TS will show an error,
//XArray["prototype"] = new Array(); will replace with native js arrray function
pop(): any { return "" };
push(val): number { return 0; };
length: number;
}
//Adding Arrray to XArray prototype chain.
XArray["prototype"] = new Array();
//our Class
class YArray extends XArray {
///Some stuff
}
var arr = new YArray();
//we can use the array prop here.
arr.push("one");
arr.push("two");
document.writeln("First Elemet in array : " + arr[0]);
document.writeln("</br>Array Lenght : " + arr.length);
Hope, this might help you!!!
希望,这可以帮助你!!!
回答by David Sherret
Starting in TypeScript 1.6, you can extend the Array type, see What's new in TypeScript
从 TypeScript 1.6 开始,您可以扩展 Array 类型,请参阅TypeScript 中的新增功能
Here's an example:
下面是一个例子:
class MyNewArray<T> extends Array<T> {
getFirst() {
return this[0];
}
}
var myArray = new MyNewArray<string>();
myArray.push("First Element");
console.log(myArray.getFirst()); // "First Element"
If you are emitting to ES5 or below, then use the following code:
如果您要发送到 ES5 或更低版本,请使用以下代码:
class MyNewArray<T> extends Array<T> {
constructor(...items: T[]) {
super(...items);
Object.setPrototypeOf(this, MyNewArray.prototype);
}
getFirst() {
return this[0];
}
}
Read more about why this is necessary here.
在这里阅读更多关于为什么这是必要的。
回答by Endre Simo
Yes it's possible to extend a native JS object in TS, however there is an issue extending built-in types (those included in lib.d.ts) like Array. Read this post for workaround: http://typescript.codeplex.com/workitem/4
是的,可以在 TS 中扩展本机 JS 对象,但是扩展内置类型(包含在 lib.d.ts 中的那些)(如 Array)存在问题。阅读这篇文章以了解解决方法:http: //typescript.codeplex.com/workitem/4
So defining a type interface which extends a native type object at a later stage can be done in the following way:
因此,可以通过以下方式定义在稍后阶段扩展本机类型对象的类型接口:
/// <reference path="lib.d.ts"/>
interface Array {
sort: (input: Array) => Array;
}
Using on a concrete example, you can sort some elements on an array which define a sort function in an interface and later implements it on an object.
使用一个具体的例子,你可以对数组中的一些元素进行排序,这些元素在接口中定义了一个排序函数,然后在一个对象上实现它。
class Math implements Array {
sort : (x: Array) => Array {
// sorting the array
}
}
var x = new Math();
x.sort([2,3,32,3]);
回答by oligofren
While researching this, I came across Ben Nadel's excellent post on Extending JavaScript Arrays While Keeping Native Bracket-Notation Functionality. After some initial confusion on how to succesfully convert this into TypeScript, I created a fully working Collection class that can be subclassed.
在研究这个的过程中,我看到了 Ben Nadel 发表的关于Extending JavaScript Arrays While Keeping Native Bracket-Notation Functionality的优秀文章。在最初对如何将其成功转换为 TypeScript 感到困惑之后,我创建了一个可以进行子类化的完全可用的 Collection 类。
It can do everything an Array can, including indexing by brackets,use in loop constructions (for, while, forEach), maps, etc.
它可以做 Array 可以做的所有事情,包括通过括号索引、在循环结构中使用(for、while、forEach)、映射等。
The main implementation points are
主要实现点是
- Create an array in the constructor, add the methods to the array and return that from the constructor
- Copy dummy declarations of Array methods to pass the
implements Arraybit
- 在构造函数中创建一个数组,将方法添加到数组并从构造函数返回
- 复制 Array 方法的虚拟声明以传递
implements Array位
Example of usage:
用法示例:
var foo = new Foo({id : 1})
var c = new Collection();
c.add(foo)
c.length === 1; // => true
foo === c[0]; // => true
foo === c.find(1); // => true
I made it available as a gist, complete with tests and an example implementation of a subclass, but I present the full source here:
我将其作为 gist 提供,包括测试和子类的示例实现,但我在此处提供了完整的源代码:
/*
* Utility "class" extending Array with lookup functions
*
* Typescript conversion of Ben Nadel's Collection class.
* https://gist.github.com/fatso83/3773d4cb5f39128b3732
*
* @author Carl-Erik Kopseng
* @author Ben Nadel (javascript original)
*/
export interface Identifiable {
getId : () => any;
}
export class Collection<T extends Identifiable> implements Array<T> {
constructor(...initialItems:any[]) {
var collection = Object.create(Array.prototype);
Collection.init(collection, initialItems, Collection.prototype);
return collection;
}
static init(collection, initialItems:any[], prototype) {
Object.getOwnPropertyNames(prototype)
.forEach((prop) => {
if (prop === 'constructor') return;
Object.defineProperty(collection, prop, { value: prototype[prop] })
});
// If we don't redefine the property, the length property is suddenly enumerable!
// Failing to do this, this would fail: Object.keys([]) === Object.keys(new Collection() )
Object.defineProperty(collection, 'length', {
value: collection.length,
writable: true,
enumerable: false
});
var itemsToPush = initialItems;
if (Array.isArray(initialItems[0]) && initialItems.length === 1) {
itemsToPush = initialItems[0];
}
Array.prototype.push.apply(collection, itemsToPush);
return collection;
}
// Find an element by checking each element's getId() method
public find(id:any):T;
// Find an element using a lookup function that
// returns true when given the right element
public find(lookupFn:(e:T) => boolean):T ;
find(x:any) {
var res, comparitor;
if (typeof x === 'function') {
comparitor = x;
} else {
comparitor = (e) => {
return e.getId() === x;
}
}
res = [].filter.call(this, comparitor);
if (res.length) return res[0];
else return null;
}
// Add an element
add(value:T);
// Adds all ements in the array (flattens it)
add(arr:T[]);
add(arr:Collection<T>);
add(value) {
// Check to see if the item is an array or a subtype thereof
if (value instanceof Array) {
// Add each sub-item using default push() method.
Array.prototype.push.apply(this, value);
} else {
// Use the default push() method.
Array.prototype.push.call(this, value);
}
// Return this object reference for method chaining.
return this;
}
remove(elem:T):boolean;
remove(lookupFn:(e:T) => boolean):boolean ;
remove(x:any):boolean {
return !!this._remove(x);
}
/**
* @return the removed element if found, else null
*/
_remove(x:any):T {
var arr = this;
var index = -1;
if (typeof x === 'function') {
for (var i = 0, len = arr.length; i < len; i++) {
if (x(this[i])) {
index = i;
break;
}
}
} else {
index = arr.indexOf(x);
}
if (index === -1) {
return null;
}
else {
var res = arr.splice(index, 1);
return res.length ? res[0] : null;
}
}
// dummy declarations
// "massaged" the Array interface definitions in lib.d.ts to fit here
toString:()=> string;
toLocaleString:()=> string;
concat:<U extends T[]>(...items:U[])=> T[];
join:(separator?:string)=> string;
pop:()=> T;
push:(...items:T[])=> number;
reverse:()=> T[];
shift:()=> T;
slice:(start?:number, end?:number)=> T[];
sort:(compareFn?:(a:T, b:T) => number)=> T[];
splice:(start?:number, deleteCount?:number, ...items:T[])=> T[];
unshift:(...items:T[])=> number;
indexOf:(searchElement:T, fromIndex?:number)=> number;
lastIndexOf:(searchElement:T, fromIndex?:number)=> number;
every:(callbackfn:(value:T, index:number, array:T[]) => boolean, thisArg?:any)=> boolean;
some:(callbackfn:(value:T, index:number, array:T[]) => boolean, thisArg?:any)=> boolean;
forEach:(callbackfn:(value:T, index:number, array:T[]) => void, thisArg?:any)=> void;
map:<U>(callbackfn:(value:T, index:number, array:T[]) => U, thisArg?:any)=> U[];
filter:(callbackfn:(value:T, index:number, array:T[]) => boolean, thisArg?:any)=> T[];
reduce:<U>(callbackfn:(previousValue:U, currentValue:T, currentIndex:number, array:T[]) => U, initialValue:U)=> U;
reduceRight:<U>(callbackfn:(previousValue:U, currentValue:T, currentIndex:number, array:T[]) => U, initialValue:U)=> U;
length:number;
[n: number]: T;
}
Of course, the bits on Identifiable, the findand removemethods are not needed, but I supply them none the less as a full fledged example is a tad more usable than a bare-bones Collection without any methods of its own.
当然,不需要Identifiable, thefind和remove方法上的位,但我仍然提供它们,因为一个完整的示例比没有任何自己的方法的准系统集合更有用。
回答by Peter Wone
Constructors that return an object implicitly substitute the value of thisfor callers of super(). Generated constructor code has to capture whatever super()returns and replace it with this.
返回对象的构造函数隐式替换 的this调用者的值super()。生成的构造函数代码必须捕获任何super()返回并将其替换为this.
Built-in classes use ES6 new.targetto do the fixup but there's no way for ES5 code to ensure that new.target has a value calling the constructor.
内置类使用 ES6new.target进行修复,但 ES5 代码无法确保 new.target 具有调用构造函数的值。
This is why your extra methods vanish - your object has the wrong prototype.
这就是为什么你的额外方法消失了——你的对象有错误的原型。
All you need to do is fix the prototype chain after calling super().
您需要做的就是在调用super().
export class RoleSet extends Array {
constructor() {
super();
Object.setPrototypeOf(this, RoleSet.prototype);
}
private containsRoleset(roleset:RoleSet){
if (this.length < roleset.length) return false;
for (var i = 0; i < roleset.length; i++) {
if (this.indexOf(roleset[i]) === -1) return false;
}
return true;
}
public contains(item: string | RoleSet): boolean {
if (item) {
return typeof item === "string" ?
this.indexOf(item) !== -1 :
this.containsRoleset(item);
} else {
return true;
}
}
}
Be aware that this curse shall afflict thy children and thy children's children until the end of code; you have to do the fixup in every generation of an inheritance chain.
请注意,这种诅咒将折磨你的孩子和你孩子的孩子,直到代码结束;您必须在继承链的每一代中进行修复。
回答by Karl
Yes you can augment the Builtin types and do it in a way that doesn't require all the paraphernalia of an XArray as described in the other answers and is closer to how you would do it in javascript.
是的,您可以增加内置类型,并以不需要其他答案中描述的 XArray 的所有用具的方式进行操作,并且更接近您在 javascript 中的操作方式。
Typescript allows a number of ways to do this, but for the Builtin types like Array and Number you need to use "merging" and declare the global namespace to augment the types, see the docs
Typescript 允许多种方法来执行此操作,但是对于诸如 Array 和 Number 之类的内置类型,您需要使用“合并”并声明全局命名空间来扩充类型,请参阅文档
so for Array we can add an optional metadata object and a get first member
所以对于 Array 我们可以添加一个可选的元数据对象和一个 get first 成员
declare global {
interface Array<T> {
meta?: any|null ,
getFirst(): T
}
}
if(!Array.prototype.meta )
{
Array.prototype.meta = null
}
if(!Array.prototype.getFirst )
{
Array.prototype.getFirst = function() {
return this[0];
}
}
we can use this like so:
我们可以这样使用它:
let myarray: number[] = [ 1,2,3 ]
myarray.meta = { desc: "first three ints" }
let first: number = myarray.getFirst()
The same goes for Number say I want to add a modulo function that isn't limited like the remainder %
Number 也是如此,说我想添加一个模函数,它不受余数 % 的限制
declare global {
interface Number {
mod(n:number): number
}
}
if(!Number.prototype.mod )
{
Number.prototype.mod = function (n: number) {
return ((this % n) + n) % n;
}
}
and we can use it like so:
我们可以像这样使用它:
let foo = 9;
console.log("-9.mod(5) is "+ foo.mod(5))
For Functions that we may want to add an API to ie to make it behave like a function and an object we can use hybrid types (see docs)
对于我们可能希望向 ie 添加 API 以使其表现得像函数和对象的函数,我们可以使用混合类型(参见文档)
// augment a (number) => string function with an API
interface Counter {
(start: number): string;
interval: number;
reset(): void;
}
//helper function to get my augmented counter function with preset values
function getCounter(): Counter {
let counter = <Counter>function (start: number) { };
counter.interval = 123;
counter.reset = function () { };
return counter;
}
use it like so:-
像这样使用它:-
let c = getCounter();
c(10);
c.reset();
c.interval = 5.0;
回答by Jani Hyyti?inen
If you already have a working Xarrayimplementation, I don't see the point in recreating it in typescript, which eventually will compile back to JavaScript.
如果您已经有一个有效的Xarray实现,我认为在 typescript 中重新创建它没有意义,它最终会编译回 JavaScript。
But I do see the point in being able to use the Xarrayin TypeScript.
但我确实看到了能够Xarray在 TypeScript 中使用 的意义。
In order to accomplish this, you simply need an interface for your Xarray. You don't even need to have a concrete implementation of your interface since your existing js implementation will serve as one.
为了实现这一点,您只需要为您的Xarray. 你甚至不需要你的接口的具体实现,因为你现有的 js 实现将作为一个实现。
interface Xarray{
apply(...arguments : any[]) : void;
//some stuff for insert, add and ...
}
declare var Xarray: {
new (...items: any[]): Xarray;
(...items: any[]): Xarray;
prototype: Array; // This should expose all the Array stuff from ECMAScript
}
After doing this, should be able to use your custom defined type through the declared variable without actually implementing it in TypeScript.
这样做之后,应该能够通过声明的变量使用您自定义的类型,而无需在 TypeScript 中实际实现它。
var xArr = new Xarray();
xArr.apply("blah", "hehe", "LOL");
You might look for reference here to see how they typed the ECMAScript Array API:
http://typescript.codeplex.com/SourceControl/changeset/view/2bee84410e02#bin/lib.d.ts
您可以在这里寻找参考以了解他们如何输入ECMAScript Array API:http:
//typescript.codeplex.com/SourceControl/changeset/view/2bee84410e02#bin/lib.d.ts
回答by Will Tomlins
In your case, a good bet would be to use this pattern:
在您的情况下,一个不错的选择是使用这种模式:
function XArray(array) {
array = array || [];
//add a new method
array.second = function second() {
return array[1];
};
//overwrite an existing method with a super type pattern
var _push = array.push;
array.push = function push() {
_push.apply(array, arguments);
console.log("pushed: ", arguments);
};
//The important line.
return array
}
Then you can do:
然后你可以这样做:
var list = XArray([3, 4]);
list.second() ; => 4
list[1] = 5;
list.second() ; => 5
note however that:
但请注意:
list.constructor ; => Array and not XArray
回答by Alexander Levakov
With purpose to overcome the problem of extension of the native Array class, I took advantage of a decorator.
为了克服原生 Array 类的扩展问题,我利用了装饰器。
function extendArray(constructor: Function) {
Object.getOwnPropertyNames(constructor.prototype)
.filter(name => name !== 'constructor')
.forEach(name => {
const attributes = Object.getOwnPropertyDescriptor(constructor.prototype, name);
Object.defineProperty(Array.prototype, name, attributes);
});
}
@extendArray
export class Collection<T> extends Array<T> {
constructor(...args: T[]) {
super(...args);
}
// my appended methods
}
BTW This decorator can be made more generic (for other native classes) if to use a decorator factory.
顺便说一句,如果使用装饰器工厂,则可以使此装饰器更通用(对于其他本机类)。
回答by Nigh7Sh4de
Don't know how frowned upon this is but for example I needed to create an array of BulletTypes so that I could cycle through them. What I did is the following:
不知道对此有多不满,但例如,我需要创建一个 BulletType 数组,以便我可以循环浏览它们。我所做的是以下内容:
interface BulletTypesArray extends Array<BulletType> {
DefaultBullet?: Bullet;
}
var BulletTypes: BulletTypesArray = [ GreenBullet, RedBullet ];
BulletTypes.DefaultBullet = GreenBullet;
Obviously you could could also make a generic interface, something like interface SuperArray<T> extends Array<T>.
显然,您也可以创建一个通用接口,例如interface SuperArray<T> extends Array<T>.

