How can I extend Array.prototype.push()?
Asked Answered
F

6

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');
Frink answered 21/2, 2009 at 7:52 Comment(0)
B
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);
    };
})();
Bichloride answered 21/2, 2009 at 8:24 Comment(11)
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 conciseVoidable
@pottedmeat: but it's less readable - remember, programs are not only written for the machine, but for other programmers as well!Janettjanetta
@Bichloride Thank you so much for a clear explaination of prototype and not!Accouter
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.Adjoining
@TedHopp Noticed the "I do not recommend that you do that."? I meant it. On the bright side, you are at the right site ;)Bichloride
I just wanted to reinforce your recommendation message by pointing out how easily severe unpleasantness can arise from ignoring it.Adjoining
Great! And how about index-based assignment? Why is there no set(index) or other method, which could raise an event?Hermaphroditism
Here's an example of a fiddle that syncs array2 with array1 using this method: jsfiddle.net/hhwcteoj/2.Subprincipal
What will happen if 2 different scripts implement such a method on the same array? Will both overrides be executed, or only the last one? I think only the last one, so how to ensure you call the previous implementation (overridden or not)?Invitatory
@JBHurteaux They will execute one after the other, in the order that they where initialized.Bichloride
Update to @TedHopp's point: In modern browsers it'll just throw an error for the maximum call stack size being exceeded, which doesn't result in anything particularly nasty.Abrahan
Y
11

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);
};
Yeasty answered 10/10, 2016 at 19:20 Comment(1)
Curious why someone wouldn't? JS has changed.Cordierite
M
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.

Mimeograph answered 21/2, 2009 at 8:20 Comment(1)
Array.prototype.push.call(this, data); wont work as Array can take multiple items. Array.prototype.push.apply(this, arguments);Tami
G
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.

Garboard answered 2/4, 2009 at 23:22 Comment(0)
A
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);
Apothecium answered 29/5, 2020 at 8:34 Comment(0)
H
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;
    }
}
Handout answered 21/2, 2015 at 15:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.