如何扩展Array.prototype.push()方法?

34

我想要扩展Array.push方法,使得使用push会触发一个回调函数,然后执行正常的数组函数。

我不太确定如何做到这一点,但是这里是我一直在尝试并且没有成功的一些代码。

arr = [];
arr.push = function(data){

    //callback method goes here

    this = Array.push(data);
    return this.length;
}

arr.push('test');
6个回答

78

由于push允许推入多个元素,因此我在下面使用arguments变量来使真正的push方法拥有所有参数。

这个解决方案只影响arr变量:

arr.push = function () {
    //Do what you want here...
    return Array.prototype.push.apply(this, arguments);
}

这个解决方案会影响所有数组。我不建议你这么做。

Array.prototype.push = (function() {
    var original = Array.prototype.push;
    return function() {
        //Do what you want here.
        return original.apply(this, arguments);
    };
})();

9
@pottedmeat:但是这样会降低可读性 - 记住,程序不仅是为机器编写的,同时也是为其他程序员编写的! 翻译:但这样写会让代码难以阅读 - 要记住,编写程序不只是为了让机器能读懂,还要让其他程序员也能轻松理解! - Christoph
7
当对Array.prototype.push进行赋值时,你必须非常小心地处理//在此处执行你想要的操作代码中的内容。如果其中任何内容导致调用Array.push()(即使是间接的),将会导致JavaScript栈溢出而崩溃。 - Ted Hopp
3
我想通过指出忽视建议会很容易引起严重不愉快来加强你的推荐信息。 - Ted Hopp
1
太好了!那么基于索引的赋值呢?为什么没有set(index)或其他可以触发事件的方法? - Fedor Alexander Steeman
1
更新至@TedHopp的观点:在现代浏览器中,当超出最大调用堆栈大小时,它只会抛出一个错误,这并不会导致任何特别严重的后果。 - Radvylf Programs
显示剩余6条评论

11

首先,您需要创建Array的子类:

ES6 (https://kangax.github.io/compat-table/es6/):

class SortedArray extends Array {
    constructor(...args) {
        super(...args);
    }
    push() {
        return super.push(arguments);
    }
}

ES5(proto 几乎已被弃用,但目前只有它是唯一的解决方案):

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

SortedArray.prototype.push = function() {
    this.arr.push(arguments);
};

8
为什么会有人不去尝试呢?JavaScript 已经改变了。 - user2467065

6

Array.prototype.push在JavaScript 1.2中被引入。它非常简单:

Array.prototype.push = function() {
    for( var i = 0, l = arguments.length; i < l; i++ ) this[this.length] = arguments[i];
    return this.length;
};

你可以在其前面添加一些内容。

6
你可以这样做:
arr = []
arr.push = function(data) {
  alert(data); //callback

  return Array.prototype.push.call(this, data);
}

如果您处于没有电话的情况下,您也可以选择这个解决方案:

arr.push = function(data) {
  alert(data); //callback
  
  //While unlikely, someone may be using "psh" to store something important
  //So we save it.
  var saved = this.psh;
  this.psh = Array.prototype.push;
  var ret = this.psh(data);
  this.psh = saved;
  return ret;
}

在我告诉你如何做的时候,你可能更好地使用另一种方法来执行回调,然后只是调用push而不是覆盖push。否则,您可能会遇到一些意外的副作用。例如,push似乎是varadic(接受可变数量的参数,类似于printf),使用上述代码将会破坏它。
您需要对_Arguments()和_ArgumentsLength()进行处理,以正确地覆盖此函数。我强烈建议不要选择这条路。
或者,您可以使用“arguments”,这也可以工作。但我仍然建议不要选择这条路。

Array.prototype.push.call(this, data); 不起作用,因为 Array 只能接受多个项目。 Array.prototype.push.apply(this, arguments); - ToughPal

1

还有一种更本地的方法可以实现这个功能: Proxy

const target = [];

const handler = {
  set: function(array, index, value) {
    // Call callback function here

    // The default behavior to store the value
    array[index] = value;

    // Indicate success
    return true;
  }
};

const proxyArray = new Proxy(target, handler);

0
我想在对象被推入数组后调用一个函数,所以我做了以下操作:

myArray.push = function() { 
    Array.prototype.push.apply(this, arguments);
    myFunction();
    return myArray.length;
};

function myFunction() {
    for (var i = 0; i < myArray.length; i++) {
        //doSomething;
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接