Javascript: присоединить прослушиватель событий к массиву для события Push () - PullRequest
29 голосов
/ 15 марта 2011

Есть ли способ узнать, когда пользователь поместил (с помощью push ()) элемент в массив?

По сути, у меня есть асинхронный скрипт, который позволяет пользователю выдвигать команды в массив. Как только мой скрипт загружается, он выполняет команды. Проблема в том, что пользователь может добавить дополнительные команды в массив после того, как мой скрипт уже запущен, и я должен быть уведомлен, когда это произойдет. Имейте в виду, что это обычный массив, который пользователь создает самостоятельно. Google Analytics делает нечто похожее на это.

Я также обнаружил, что именно здесь, как мне кажется, Google делает это, но я не совсем понимаю код: return Object.prototype [ha] .call (Object (k)) == "[массив объектов]"

Я также нашел отличный пример, который, кажется, охватывает основы, но я не могу заставить мой добавленный метод push работать правильно: http://jsbin.com/ixovi4/4/edit

Ответы [ 11 ]

27 голосов
/ 11 сентября 2014

Вы можете использовать функцию Eventify, которая переопределяет push в переданном массиве.

var eventify = function(arr, callback) {
    arr.push = function(e) {
        Array.prototype.push.call(arr, e);
        callback(arr);
    };
};

В следующем примере должно быть выдано 3 оповещения, поскольку именно так поступает обработчик событий (обратный вызов) после вызова функции eventify.

var testArr = [1, 2];

testArr.push(3);

eventify(testArr, function(updatedArr) {
  alert(updatedArr.length);
});

testArr.push(4);
testArr.push(5);
testArr.push(6);
22 голосов
/ 15 марта 2011

Единственный разумный способ сделать это - написать класс, обернутый вокруг массива:

function EventedArray(handler) {
   this.stack = [];
   this.mutationHandler = handler || function() {};
   this.setHandler = function(f) {
      this.mutationHandler = f;
   };
   this.callHandler = function() { 
      if(typeof this.mutationHandler === 'function') {
         this.mutationHandler();
      }
   };
   this.push = function(obj) {
      this.stack.push(obj);
      this.callHandler();
   };
   this.pop = function() {
      this.callHandler();
      return this.stack.pop();
   };
   this.getArray = function() {
      return this.stack;
   }
}

var handler = function() {
   console.log('something changed');
};

var arr = new EventedArray(handler);

//or 

var arr = new EventedArray();
arr.setHandler(handler);


arr.push('something interesting'); //logs 'something changed'
13 голосов
/ 24 марта 2011

попробуйте это:

var MyArray = function() { };
MyArray.prototype = Array.prototype;
MyArray.prototype.push = function() {
    console.log('push now!');
    for(var i = 0; i < arguments.length; i++ ) {
        Array.prototype.push.call(this, arguments[i]);
    }
};

var arr = new MyArray();
arr.push(2,3,'test',1);

Вы можете добавить функции после или после нажатия

3 голосов
/ 17 июля 2014

Почему бы просто не сделать что-то подобное?

Array.prototype.eventPush = function(item, callback) {
  this.push(item);
  callback(this);
}

Затем определить обработчик.

handler = function(array) {
    console.log(array.length)
}

Затем использовать eventPush в том месте, где вы хотите, чтобы конкретное событие происходилов обработчике так:

a = []
a.eventPush(1, handler);
a.eventPush(2, handler);
2 голосов
/ 22 марта 2016

Я бы обернул исходный массив вокруг простого интерфейса наблюдателя следующим образом.

function EventedList(list){
    this.listbase = list;
    this.events = [];
}

EventedList.prototype.on = function(name, callback){
    this.events.push({
        name:name,
        callback:callback
    });
}

//push to listbase and emit added event
EventedList.prototype.push = function(item){
    this.listbase.push(item);
    this._emit("added", item)
}

EventedList.prototype._emit = function(evtName, data){
    this.events.forEach(function(event){
        if(evtName === event.name){
            event.callback.call(null, data, this.listbase);
        }
    }.bind(this));
}

Затем я бы создал его с помощью базового массива

    //returns an object interface that lets you observe the array
    var evtList = new EventedList([]);

    //attach a listener to the added event
    evtList.on('added', function(item, list){
         console.log("added event called: item = "+ item +", baseArray = "+ list);
    })

    evtList.push(1) //added event called: item = 1, baseArray = 1
    evtList.push(2) //added event called: item = 2, baseArray = 1,2
    evtList.push(3) //added event called: item = 3, baseArray = 1,2,3

, который вы также можете расширитьнаблюдатель, чтобы наблюдать другие вещи, такие как prePush или postPush или любые другие события, которые вы хотите генерировать при взаимодействии с внутренним базовым массивом.

1 голос
/ 13 октября 2017

Иногда вам нужно поставить в очередь все, прежде чем обратный вызов станет доступен. Это решает эту проблему. Вставьте любой элемент (ы) в массив. Как только вы захотите начать использовать эти элементы, передайте массив и обратный вызов QueuedCallback(). QueuedCallback перегрузит array.push в качестве вашего обратного вызова, а затем перебирает все элементы в очереди. Продолжайте помещать элементы в этот массив, и они будут перенаправлены прямо на ваш обратный вызов. Массив останется пустым.

Совместим со всеми браузерами и IE 5.5 +.

var QueuedCallback = function(arr, callback) {
  arr.push = callback;
  while (arr.length) callback(arr.shift());
};

Пример использования здесь .

1 голос
/ 26 декабря 2014

Это добавит функцию с именем onPush во все массивы, по умолчанию она не должна ничего делать, поэтому она не мешает нормальному функционированию массивов.

просто переопределяет onPush в отдельном массиве.

Array.prototype.oldPush = Array.prototype.push;
Array.prototype.push = function(obj){
    this.onPush(obj);
    this.oldPush(obj);
};
//Override this method, be default this shouldnt do anything. As an example it will.
Array.prototype.onPush = function(obj){
    alert(obj + 'got pushed');
};

//Example
var someArray = [];

//Overriding
someArray.onPush = function(obj){
    alert('somearray now has a ' + obj + ' in it');
};

//Testing
someArray.push('swag');

Это предупреждение 'у somearray теперь есть свэг'

0 голосов
/ 10 января 2016

для целей отладки вы можете попробовать. И отслеживать вызывающую функцию из стека вызовов.

yourArray.push = function(){debugger;}
0 голосов
/ 06 января 2016

Если вы хотите сделать это для одного массива:

var a = [];

a.push = function(item) {
    Array.prototype.push.call(this, item);
    this.onPush(item);
};

a.onPush = function(obj) {
    // Do your stuff here (ex: alert(this.length);)
};
0 голосов
/ 21 ноября 2013

Намного лучше использовать тот факт, что эти методы изменяют длину массива. Способ воспользоваться этим довольно прост (CoffeeScript):

class app.ArrayModelWrapper extends Array
  constructor: (arr,chName,path)->
    vl  = arr.length
    @.push.apply(@,arr)
    Object.defineProperty(@,"length",{
      get: ->vl
      set: (newValue)=>
        console.log("Hello there ;)")
        vl = newValue
        vl
      enumerable: false
    })
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...