42

我尝试使用一些用户友好的方法(例如 Array.Add() 而不是 Array.push() 等)在 javascript 中扩展 Array 对象...

我实现了 3 种方法来做到这一点。不幸的是,第三种方式不起作用,我想问为什么?以及如何做到这一点。

//------------- 1st way
Array.prototype.Add=function(element){
     this.push(element);
};

var list1 = new Array();
list1.Add("Hello world");
alert(list1[0]);

//------------- 2nd way
function Array2 () {
    //some other properties and methods
};

Array2.prototype = new Array;
Array2.prototype.Add = function(element){
  this.push(element);  
};

var list2 = new Array2;
list2.Add(123);
alert(list2[0]);

//------------- 3rd way
function Array3 () {
    this.prototype = new Array;
    this.Add = function(element){
      this.push(element);  
    };
};

var list3 = new Array3;
list3.Add(456);  //push is not a function
alert(list3[0]); // undefined

在第三种方式中,我想在 Array3 类内部扩展 Array 对象。如何做到这一点才不会得到“推送不是函数”和“未定义”?

在这里,我添加了第 4 种方式。

//------------- 4th way
function Array4 () {
    //some other properties and methods
    this.Add = function(element){
        this.push(element);
    };
 };
Array4.prototype = new Array();

var list4 = new Array4();
list4.Add(789);
alert(list4[0]);

在这里我必须再次使用原型。我希望避免在类构造函数之外使用额外的行作为 Array4.prototype。我想有一个紧凑的定义类,所有部分都在一个地方。但我认为我不能这样做。

4

8 回答 8

33

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

使用__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
于 2015-05-22T08:28:40.187 回答
32

方法名称应该是小写的。不应在构造函数中修改原型。

function Array3() { };
Array3.prototype = new Array;
Array3.prototype.add = Array3.prototype.push

在 CoffeeScript 中

class Array3 extends Array
   add: (item)->
     @push(item) 

如果您不喜欢该语法,并且您必须从构造函数中扩展它,您唯一的选择是:

// define this once somewhere
// you can also change this to accept multiple arguments 
function extend(x, y){
    for(var key in y) {
        if (y.hasOwnProperty(key)) {
            x[key] = y[key];
        }
    }
    return x;
}


function Array3() { 
   extend(this, Array.prototype);
   extend(this, {
      Add: function(item) {
        return this.push(item)
      }

   });
};

你也可以这样做

ArrayExtenstions = {
   Add: function() {

   }
}
extend(ArrayExtenstions, Array.prototype);



function Array3() { }
Array3.prototype = ArrayExtenstions;

在过去,'prototype.js' 曾经有一个 Class.create 方法。你可以把这一切都包装起来就是这样的方法

var Array3 = Class.create(Array, {
    construct: function() {

    },    
    Add: function() {

    }
});

有关此内容以及如何实现的更多信息,请查看prototype.js 源代码

于 2012-07-05T04:54:24.577 回答
4

A while ago I read the book Javascript Ninja written by John Resig, the creator of jQuery. He proposed a way to mimic array-like methods with a plain JS object. Basically, only length is required.

var obj = {
    length: 0, //only length is required to mimic an Array
    add: function(elem){
        Array.prototype.push.call(this, elem);
    },
    filter: function(callback) {
        return Array.prototype.filter.call(this, callback); //or provide your own implemetation
    }
};

obj.add('a');
obj.add('b');
console.log(obj.length); //2
console.log(obj[0], obj[1]); //'a', 'b'

I don't mean it's good or bad. It's an original way of doing Array operations. The benefit is that you do not extend the Array prototype. Keep in mind that obj is a plain object, it's not an Array. Therefore obj instanceof Array will return false. Think obj as a façade.

If that code is of interest to you, read the excerpt Listing 4.10 Simulating array-like methods.

于 2016-04-12T21:22:11.593 回答
2

在您的第三个示例中,您只是创建了一个以prototypeobject命名的新属性Array3。当您执行new Array3which should benew Array3()时,您将该对象实例化为 variable list3。因此,该Add方法将不起作用this,因为有问题的对象 没有有效的方法push。希望你能理解。

编辑:查看了解 JavaScript 上下文以了解更多关于this.

于 2012-07-05T04:35:01.663 回答
1

你也可以在 ES6 中使用这种方式:

Object.assign(Array.prototype, {
    unique() {
      return this.filter((value, index, array) => {
        return array.indexOf(value) === index;
      });
    }
});

结果:

let x = [0,1,2,3,2,3];
let y = x.unique();
console.log(y); // => [0,1,2,3]
于 2016-08-08T15:41:14.907 回答
0

您是否尝试做一些更复杂的事情,然后只为“push”添加一个名为“Add”的别名?

如果没有,最好避免这样做。我建议这是一个坏主意的原因是因为 Array 是一个内置的 javascript 类型,修改它会导致所有脚本 Array 类型都有新的“添加”方法。名称与另一个第三方发生冲突的可能性很高,并且可能导致第三方脚本失去其方法而有利于您的脚本。

我的一般规则是让一个辅助函数在 Array 上工作,如果它不存在于某个地方,并且仅在非常必要时扩展 Array。

于 2012-07-05T04:53:16.813 回答
0

您不能在 JavaScript 中扩展数组对象。

相反,您可以做的是定义一个对象,该对象将包含在 Array 上执行的函数列表,并将这些函数注入该 Array 实例并返回这个新的 Array 实例。您不应该做的是更改Array.prototype以在列表中包含您的自定义函数。

例子:

function MyArray() {
  var tmp_array = Object.create(Array.prototype);
  tmp_array = (Array.apply(tmp_array, arguments) || tmp_array);
  //Now extend tmp_array
  for( var meth in MyArray.prototype )
    if(MyArray.prototype.hasOwnProperty(meth))
      tmp_array[meth] = MyArray.prototype[meth];
  return (tmp_array);
}
//Now define the prototype chain.
MyArray.prototype = {
  customFunction: function() { return "blah blah"; },
  customMetaData: "Blah Blah",
}

只是一个示例代码,您可以修改它并根据需要使用它。但我建议您遵循的基本概念保持不变。

于 2013-05-07T20:42:20.330 回答
0
var SubArray = function() {                                           
    var arrInst = new Array(...arguments); // spread arguments object
    /* Object.getPrototypeOf(arrInst) === Array.prototype */
    Object.setPrototypeOf(arrInst, SubArray.prototype);     //redirectionA
    return arrInst; // now instanceof SubArray
};

SubArray.prototype = {
    // SubArray.prototype.constructor = SubArray;
    constructor: SubArray,

    // methods avilable for all instances of SubArray
    add: function(element){return this.push(element);},
    ...
};

Object.setPrototypeOf(SubArray.prototype, Array.prototype); //redirectionB

var subArr = new SubArray(1, 2);
subArr.add(3); subArr[2]; // 3

答案是一个紧凑的解决方法,它可以在所有支持的浏览器中按预期工作。

于 2017-05-28T18:14:25.453 回答