views:

2399

answers:

4

I'm trying to extend the Array.push method so that using push will trigger a callback method, 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');
+1  A: 

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

Edit:

While I'm telling you how to do it, you might be better served with using a different method that performs the callback 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.

Edit once more: Or you could use "arguments", that'd work too. Still advise against taking this route though.

Patrick
+13  A: 

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);
    };
})();
some
My favorite form of function wrapping is to pass Array.prototype.push when you execute your outer function, and accept it in the function as the variable original. It's a little more concise
pottedmeat
@pottedmeat: but it's less readable - remember, programs are not only written for the machine, but for other programmers as well!
Christoph
A: 

I would do this:

var callback = function() { alert("called"); };
var original = Array.prototype.push;
Array.prototype.push = function()
{
  callback();
  return original.apply(this, arguments);
};

If you wanted an argument to be a callback, you could do this:

var original = Array.prototype.push;
Array.prototype.push = function(callback)
{
  callback();
  return original.apply(this, Array.prototype.slice.call(arguments, 1));
}

These both have been tested.

cdmckay
@cdmckay: If you had tested it, you would have noticed that you have an created an infinite loop...
some
@some My bad, I wrote that late last night. I've fixed and tested them.
cdmckay
@cdmckay: Now it works, but you pollute the global namespace with 'original', and if some other function change the value of 'original' you have broken the array push. Your second solution makes no sense: If I must supply the callback when I push, I can call the callback myself.
some
@cdmckay: By the way, it's not me who have voted you down.
some
@some: I don't mind be voted down, my original answer was an infinite loop :)
cdmckay
+1  A: 

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.