代码之家  ›  专栏  ›  技术社区  ›  Tauren

子类化Javascript数组。TypeError:Array.prototype.toString不是泛型

  •  32
  • Tauren  · 技术社区  · 15 年前

    是否可以从javascript数组子类化和继承?

    我想拥有自己的自定义数组对象,它具有数组的所有特性,但包含其他属性。我会用 myobj instanceof CustomArray

    在尝试子类并遇到一些问题之后,我发现 Dean Edwards 文章指出使用数组对象执行此操作不正确。结果是InternetExplorer处理得不好。但我也发现了其他问题(目前为止只在Chrome中进行了测试)。

    下面是一些示例代码:

    /** 
     *  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);
    

    在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]
    

    显然,对象的行为不一样。我应该放弃这种方法,还是有办法实现我的目标 自定义数组的myobj实例 ?

    6 回复  |  直到 15 年前
        1
  •  36
  •   Community Mohan Dere    9 年前

    朱里扎伊采夫( @kangax )就在今天发表了一篇关于这个主题的非常好的文章。

    他探索了各种不同的选择,比如迪恩·爱德华兹 iframe借用

        2
  •  23
  •   Community Mohan Dere    5 年前

    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
    

    原始答案:(不推荐,可能导致 performance issues

    复制粘贴自 article

    使用 __proto__

    function SubArray() {
      var arr = [ ];
      arr.push.apply(arr, arguments);
      arr.__proto__ = SubArray.prototype;
      return arr;
    }
    SubArray.prototype = new Array;
    

    SubArray

    SubArray.prototype.last = function() {
      return this[this.length - 1];
    };
    

    var sub = new SubArray(1, 2, 3);
    

    行为类似于普通数组

    sub instanceof SubArray; // true
    sub instanceof Array; // true
    
        3
  •  1
  •   Matchu    15 年前

    Array.prototype 方法内部。这个 CustomArray push 和自定义方法 last

    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
    

    任何想要拉入自定义实现的数组方法都必须手动实现,尽管您可能只是聪明地使用循环,因为在我们的自定义实现中会发生什么 很普通。

        4
  •  1
  •   termi    11 年前

    签出这个。它在所有支持 __原型__

    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
    
        5
  •  1
  •   Derek Ziemba    9 年前

    • 从本机数组类继承所有原型和属性。

    如果可以使用ES6,则应该使用 class SubArray extends Array 方法LaggingReflection已发布。

    下面是对数组进行子类化和继承的要点。下面这个节选是完整的例子。

    ///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);
    

    ///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);
    

    下面是一些使用示例和测试。

    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
    
        6
  •  0
  •   Amit Portnoy    11 年前

    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;
      };
    };
    

    写完你自己的 SubArray 类可以使其继承数组,如下所示:

    var SubArrayFactory = toArraySubClassFactory(SubArray);
    
    var mySubArrayInstance = SubArrayFactory(/*whatever SubArray constructor takes*/)
    
        7
  •  0
  •   Ciro Santilli OurBigBook.com    6 年前

    使用下述技术: How can I extend the Array class and keep its implementations 我们可以达到:

    #!/usr/bin/env node
    
    const assert = require('assert');
    
    class MyArray extends Array {
      constructor(nodes, myint) {
        super(...nodes);
        this.myint = myint;
      }
    
      static get [Symbol.species]() {
        return Object.assign(function (...items) {
          return new MyArray(new Array(...items))
        }, MyArray);
      }
    
      inc() { return this.myint + 1; }
    }
    
    const my_array = new MyArray([2, 3, 5], 9);
    assert(my_array[0] === 2);
    assert(my_array[1] === 3);
    assert(my_array[2] === 5);
    
    assert(my_array.myint === 9);
    
    assert(my_array.inc() === 10);
    
    assert(my_array.toString() === '2,3,5');
    
    my_slice = my_array.slice(1, 2);
    assert(my_slice[0] === 3);
    assert(my_slice.constructor === MyArray);
    
    

    获取索引符号 [] Arrray 被问及: Implement Array-like behavior in JavaScript without using Array

    在Node.js v10.15.1中测试。