34

I'm trying to extend the Array.push method so that using push will trigger a callback method and then perform the normal array function.

I'm not quite sure how to do this, but here's some code I've been playing with unsuccessfully.

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

    //callback method goes here

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

arr.push('test');

6 Answers 6

78

Since push allows more than one element to be pushed, I use the arguments variable below to let the real push method have all arguments.

This solution only affects the arr variable:

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

This solution affects all arrays. I do not recommend that you do that.

Array.prototype.push = (function() {
    var original = Array.prototype.push;
    return function() {
        //Do what you want here.
        return original.apply(this, arguments);
    };
})();
Sign up to request clarification or add additional context in comments.

11 Comments

@pottedmeat: but it's less readable - remember, programs are not only written for the machine, but for other programmers as well!
@some Thank you so much for a clear explaination of prototype and not!
When assigning to Array.prototype.push, you have to be extremely careful about what goes into the //Do what you want here code. If anything in it causes a call to Array.push() (even indirectly), you'll crash JavaScript with a stack overflow.
I just wanted to reinforce your recommendation message by pointing out how easily severe unpleasantness can arise from ignoring it.
Great! And how about index-based assignment? Why is there no set(index) or other method, which could raise an event?
|
12

First you need subclass Array:

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

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

ES5 (proto is almost deprecated, but it is the only solution for now):

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

1 Comment

Curious why someone wouldn't? JS has changed.
6

Array.prototype.push was introduced in JavaScript 1.2. It is really as simple as this:

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

You could always add something in the front of that.

Comments

6

You could do it this way:

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

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

If you're in a situation without call, you could also go for this solution:

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

While I'm telling you how to do it, you might be better served with using a different method that performs the callback and then just calls push on the array rather than overriding push. You may end up with some unexpected side effects. For instance, push appears to be varadic (takes a variable number of arguments, like printf), and using the above would break that.

You'd need to do mess with _Arguments() and _ArgumentsLength() to properly override this function. I highly suggest against this route.

Or you could use "arguments", and that'd work too. I still advise against taking this route though.

1 Comment

Array.prototype.push.call(this, data); wont work as Array can take multiple items. Array.prototype.push.apply(this, arguments);
1

There's another, more native method to achieve this: 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);

Comments

0

I wanted to call a function after the object has been pushed to the array, so I did the following:

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

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

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.