扩展本机JavaScript数组

Bal*_*an웃 30 javascript typescript

有没有办法从JS本机函数继承一个类?

例如,我有一个像这样的JS函数:

function Xarray()
{
    Array.apply(this, arguments);
    //some stuff for insert, add and remove notification
}
Xarray.prototype = new Array();
Run Code Online (Sandbox Code Playgroud)

我试图将它转换为Typescript但我失败了!!

export class Xarray implements Array {
}
Run Code Online (Sandbox Code Playgroud)

编译器要求我定义所有Array接口属性.我知道如果我需要这个Xarray.prototype = new Array();,我必须Array在TS中扩展.

如何在TS中扩展JS本机对象?

Dav*_*ret 41

从TypeScript 1.6开始,您可以扩展Array类型,请参阅TypeScript中的新增内容

这是一个例子:

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"
Run Code Online (Sandbox Code Playgroud)

如果您要发送到ES5或更低版本,请使用以下代码:

class MyNewArray<T> extends Array<T> {
    constructor(...items: T[]) {
        super(...items);
        Object.setPrototypeOf(this, MyNewArray.prototype);
    }

    getFirst() {
        return this[0];
    }
}
Run Code Online (Sandbox Code Playgroud)

在这里阅读更多关于为什么这是必要的.

  • 从臀部拍摄@SimonEpskamp,您的编辑目标是ES5.请参阅下面的答案,为什么我这么认为.尝试定位ES6,如果它开始工作,这个猜测就会得到确认.如果是这样,那么如何使它与ES5一起使用的细节就在答案中. (2认同)

Raj*_*l 웃 12

我认为没有办法继承像Array这样的现有接口,

export class Xarray implements Array {

}
Run Code Online (Sandbox Code Playgroud)

您应该创建一个函数并使用其原型继承它.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();
Run Code Online (Sandbox Code Playgroud)

更新:这个问题很好地讨论并在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);
Run Code Online (Sandbox Code Playgroud)

希望,这可能对你有所帮助!


End*_*imo 8

是的,可以在TS中扩展本机JS对象,但是存在扩展内置类型(lib.d.ts中包含的类型)的问题,例如Array.阅读这篇文章以获得解决方法:http://typescript.codeplex.com/workitem/4

因此,可以通过以下方式定义在稍后阶段扩展本机类型对象的类型接口:

/// <reference path="lib.d.ts"/>
interface Array {
    sort: (input: Array) => Array;
}
Run Code Online (Sandbox Code Playgroud)

使用具体示例,您可以对数组中的某些元素进行排序,这些元素在接口中定义排序函数,然后在对象上实现它.

class Math implements Array {
    sort : (x: Array) => Array {
          // sorting the array
    }
}
var x = new Math();
x.sort([2,3,32,3]);
Run Code Online (Sandbox Code Playgroud)

  • 你不能只在那里转储sort(),因为这也需要实现所有其他的Array函数.此外,"Math"已经作为接口而不是类存在. (6认同)

oli*_*ren 5

在研究这个问题时,我偶然发现了Ben Nadel关于在保持原生括号表示功能的同时扩展JavaScript数组的优秀文章.在对如何将其成功转换为TypeScript的初步混淆之后,我创建了一个可以进行子类化的完全工作的Collection类.

它可以完成Array所能做的所有事情,包括用括号括起来,在循环结构中使用(for,while,forEach),map等.

主要实施点是

  1. 在构造函数中创建一个数组,将方法添加到数组并从构造函数返回该数组
  2. 复制Array方法的虚拟声明以传递该implements Array

用法示例:

  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
Run Code Online (Sandbox Code Playgroud)

我把它作为一个要点提供,完成了测试和一个子类的示例实现,但我在这里提供完整的源代码:

/*
 * 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;
}
Run Code Online (Sandbox Code Playgroud)

当然,不需要比特Identifiable,方法findremove方法,但我提供它们,因为完全成熟的例子比没有任何方法的裸骨集合更有用.