Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/425.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
子类化Javascript数组。TypeError:Array.prototype.toString不是泛型_Javascript_Inheritance - Fatal编程技术网

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

子类化Javascript数组。TypeError:Array.prototype.toString不是泛型,javascript,inheritance,Javascript,Inheritance,是否可以从javascript数组中进行子类化和继承 我希望拥有自己的自定义数组对象,该对象具有数组的所有功能,但包含其他属性。如果实例是我的CustomArray,我会使用CustomArray的myobj instanceof执行特定操作 在尝试子类化并遇到一些问题后,我发现这篇文章指出使用数组对象进行此操作不正确。结果是Internet Explorer无法正确处理它。但我也发现了其他问题,目前仅在Chrome上测试过 下面是一些示例代码: /** * Inherit the pro

是否可以从javascript数组中进行子类化和继承

我希望拥有自己的自定义数组对象,该对象具有数组的所有功能,但包含其他属性。如果实例是我的CustomArray,我会使用CustomArray的myobj instanceof执行特定操作

在尝试子类化并遇到一些问题后,我发现这篇文章指出使用数组对象进行此操作不正确。结果是Internet Explorer无法正确处理它。但我也发现了其他问题,目前仅在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 instanceof CustomArray目标?

我以前尝试过这样做;一般来说,这不会发生。不过,您可以通过在内部应用Array.prototype方法来伪造它。这个CustomArray类虽然只在Chrome中测试过,但最后实现了标准推送和自定义方法。不知何故,我在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
任何想要引入自定义实现的数组方法都必须手动实现,尽管您可能很聪明,可以使用循环,因为在自定义推送中发生的事情非常通用。

Juriy Zaytsev今天刚刚发布了一篇关于此主题的非常好的文章

他探索了各种替代方法,如Dean Edwards iframe借用技术、直接对象扩展、原型扩展和ECMAScript 5访问器属性的使用

最终没有完美的实现,每一个都有自己的优点和缺点

绝对是一本非常好的读物:


看看这个。它在所有支持“proto”的浏览器中都能正常工作


我创建了一个简单的NPM模块来解决这个问题。其基本功能如下:

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;
  };
};
编写自己的子数组类后,可以按如下方式使其继承数组:

var SubArrayFactory = toArraySubClassFactory(SubArray);

var mySubArrayInstance = SubArrayFactory(/*whatever SubArray constructor takes*/)
ES6 原始答案:不推荐,可能会导致

复制已接受答案中提到的粘贴,以提高可见性

使用_uproto__ 现在您可以将您的方法添加到子阵列

像普通数组一样初始化

var sub = new SubArray(1, 2, 3);
sub instanceof SubArray; // true
sub instanceof Array; // true
行为类似于普通数组

var sub = new SubArray(1, 2, 3);
sub instanceof SubArray; // true
sub instanceof Array; // true

这里有一个完整的例子,可以在ie9和更高版本上使用。对于ES6,使用自定义构造函数的最小可运行示例

如果还希望重写构造函数,则需要格外小心,因为某些方法将需要旧的构造函数

使用:中提到的技术,我们可以达到:

#!/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的情况下获取索引符号[]:


在Node.js v10.15.1中测试。

创建包装器是一个可接受的解决方案吗?上面的问题是Array.apply正在返回一个新对象,而忽略了this上下文。@Anurag:通过包装器,您的意思是这样做吗?还是你有别的想法?很棒的文章和完美的时机!谢谢。即使这篇文章确实很酷,我们也应该避免仅仅用一个链接来回答@LaggingReflect answer完成了将文章中的答案带到StackOverflow的工作。谢谢,但是这个解决方案并没有真正创建一个性能类似于数组的对象。您可以向它添加方法,就像使用push一样,但它不处理直接索引操作。例如,执行[5]=6不会像在实际数组中那样更改长度。@CMS-answer中链接的文章回顾了所有可能的解决方案,并指出了缺陷。@Tauren-aha,我知道我遗漏了一些明显的东西:整洁的文章-可惜我以前没有找到它!此处此CustomArray的jsperf:1。2.这非常有效,对于初学者来说很有意义。但是Array.isArray返回真值吗?我检查过了,它没有解决方案,我只修改了一个部分。不必在外部定义原型,您可以在构造函数内部使用此函数进行定义。prototype=newArray不确定这是否是一种错误的做法though@yosefrow函数中的这个对象实际上是用new调用函数的对象,它只是一个空对象,对象没有prototype属性,它们的构造函数有。另外,因为我们从子数组函数返回一个完全自构造的对象arr=[],所以修改它并不重要。我假设this.prototype指的是返回的任何对象的原型。但现在我明白你的意思了,因为这根本没有被归还,所以你对这件事做什么无关紧要
sub instanceof SubArray; // true
sub instanceof Array; // true
///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
#!/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);