function ObservableArray(items) {
var _self = this,
_array = [],
_handlers = {
itemadded: [],
itemremoved: [],
itemset: []
};
function defineIndexProperty(index) {
if (!(index in _self)) {
Object.defineProperty(_self, index, {
configurable: true,
enumerable: true,
get: function() {
return _array[index];
},
set: function(v) {
_array[index] = v;
raiseEvent({
type: "itemset",
index: index,
item: v
});
}
});
}
}
function raiseEvent(event) {
_handlers[event.type].forEach(function(h) {
h.call(_self, event);
});
}
Object.defineProperty(_self, "addEventListener", {
configurable: false,
enumerable: false,
writable: false,
value: function(eventName, handler) {
eventName = ("" + eventName).toLowerCase();
if (!(eventName in _handlers)) throw new Error("Invalid event name.");
if (typeof handler !== "function") throw new Error("Invalid handler.");
_handlers[eventName].push(handler);
}
});
Object.defineProperty(_self, "removeEventListener", {
configurable: false,
enumerable: false,
writable: false,
value: function(eventName, handler) {
eventName = ("" + eventName).toLowerCase();
if (!(eventName in _handlers)) throw new Error("Invalid event name.");
if (typeof handler !== "function") throw new Error("Invalid handler.");
var h = _handlers[eventName];
var ln = h.length;
while (--ln >= 0) {
if (h[ln] === handler) {
h.splice(ln, 1);
}
}
}
});
Object.defineProperty(_self, "push", {
configurable: false,
enumerable: false,
writable: false,
value: function() {
var index;
for (var i = 0, ln = arguments.length; i < ln; i++) {
index = _array.length;
_array.push(arguments[i]);
defineIndexProperty(index);
raiseEvent({
type: "itemadded",
index: index,
item: arguments[i]
});
}
return _array.length;
}
});
Object.defineProperty(_self, "pop", {
configurable: false,
enumerable: false,
writable: false,
value: function() {
if (_array.length > -1) {
var index = _array.length - 1,
item = _array.pop();
delete _self[index];
raiseEvent({
type: "itemremoved",
index: index,
item: item
});
return item;
}
}
});
Object.defineProperty(_self, "unshift", {
configurable: false,
enumerable: false,
writable: false,
value: function() {
for (var i = 0, ln = arguments.length; i < ln; i++) {
_array.splice(i, 0, arguments[i]);
defineIndexProperty(_array.length - 1);
raiseEvent({
type: "itemadded",
index: i,
item: arguments[i]
});
}
for (; i < _array.length; i++) {
raiseEvent({
type: "itemset",
index: i,
item: _array[i]
});
}
return _array.length;
}
});
Object.defineProperty(_self, "shift", {
configurable: false,
enumerable: false,
writable: false,
value: function() {
if (_array.length > -1) {
var item = _array.shift();
delete _self[_array.length];
raiseEvent({
type: "itemremoved",
index: 0,
item: item
});
return item;
}
}
});
Object.defineProperty(_self, "splice", {
configurable: false,
enumerable: false,
writable: false,
value: function(index, howMany /*, element1, element2, ... */ ) {
var removed = [],
item,
pos;
index = index == null ? 0 : index < 0 ? _array.length + index : index;
howMany = howMany == null ? _array.length - index : howMany > 0 ? howMany : 0;
while (howMany--) {
item = _array.splice(index, 1)[0];
removed.push(item);
delete _self[_array.length];
raiseEvent({
type: "itemremoved",
index: index + removed.length - 1,
item: item
});
}
for (var i = 2, ln = arguments.length; i < ln; i++) {
_array.splice(index, 0, arguments[i]);
defineIndexProperty(_array.length - 1);
raiseEvent({
type: "itemadded",
index: index,
item: arguments[i]
});
index++;
}
return removed;
}
});
Object.defineProperty(_self, "length", {
configurable: false,
enumerable: false,
get: function() {
return _array.length;
},
set: function(value) {
var n = Number(value);
var length = _array.length;
if (n % 1 === 0 && n >= 0) {
if (n < length) {
_self.splice(n);
} else if (n > length) {
_self.push.apply(_self, new Array(n - length));
}
} else {
throw new RangeError("Invalid array length");
}
_array.length = n;
return value;
}
});
Object.getOwnPropertyNames(Array.prototype).forEach(function(name) {
if (!(name in _self)) {
Object.defineProperty(_self, name, {
configurable: false,
enumerable: false,
writable: false,
value: Array.prototype[name]
});
}
});
if (items instanceof Array) {
_self.push.apply(_self, items);
}
}
(function testing() {
var x = new ObservableArray(["a", "b", "c", "d"]);
console.log("original array: %o", x.slice());
x.addEventListener("itemadded", function(e) {
console.log("Added %o at index %d.", e.item, e.index);
});
x.addEventListener("itemset", function(e) {
console.log("Set index %d to %o.", e.index, e.item);
});
x.addEventListener("itemremoved", function(e) {
console.log("Removed %o at index %d.", e.item, e.index);
});
console.log("popping and unshifting...");
x.unshift(x.pop());
console.log("updated array: %o", x.slice());
console.log("reversing array...");
console.log("updated array: %o", x.reverse().slice());
console.log("splicing...");
x.splice(1, 2, "x");
console.log("setting index 2...");
x[2] = "foo";
console.log("setting length to 10...");
x.length = 10;
console.log("updated array: %o", x.slice());
console.log("setting length to 2...");
x.length = 2;
console.log("extracting first element via shift()");
x.shift();
console.log("updated array: %o", x.slice());
})();
set(index)
dans le prototype d'Array et faire quelque chose comme l'antisanité ditEn lisant toutes les réponses ici, j'ai assemblé une solution simplifiée qui ne nécessite aucune bibliothèque externe.
Il illustre également beaucoup mieux l'idée générale de l'approche:
la source
push
renvoie lelength
du tableau. Ainsi, vous pouvez obtenir la valeur renvoyée parArray.prototype.push.apply
à une variable et la renvoyer à partir de lapush
fonction personnalisée .J'ai trouvé ce qui suit qui semble accomplir cela: https://github.com/mennovanslooten/Observable-Arrays
Observable-Arrays étend le trait de soulignement et peut être utilisé comme suit: (à partir de cette page)
la source
arr[2] = "foo"
, la notification de changement est asynchrone . Étant donné que JS ne fournit aucun moyen de surveiller de tels changements, cette bibliothèque repose sur un délai d'expiration qui s'exécute toutes les 250 ms et vérifie si le tableau a changé du tout - vous ne recevrez donc pas de notification de modification avant le prochain heure d'expiration du délai.push()
Cependant, d' autres changements comme être notifié immédiatement (de manière synchrone).J'ai utilisé le code suivant pour écouter les modifications apportées à un tableau.
J'espère que cela a été utile :)
la source
La solution de méthode push Override la plus votée de @canon a des effets secondaires qui n'étaient pas pratiques dans mon cas:
Cela rend le descripteur de propriété push différent (
writable
etconfigurable
doit être défini à latrue
place defalse
), ce qui provoque des exceptions ultérieurement.Il déclenche l'événement plusieurs fois lorsqu'il
push()
est appelé une fois avec plusieurs arguments (tels quemyArray.push("a", "b")
), ce qui dans mon cas était inutile et mauvais pour les performances.C'est donc la meilleure solution que j'ai pu trouver pour résoudre les problèmes précédents et qui est à mon avis plus propre / plus simple / plus facile à comprendre.
S'il vous plaît voir les commentaires pour mes sources et pour des conseils sur la façon d'implémenter les autres fonctions de mutation en dehors de push: 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'.
la source
...
syntaxe similaire , et qui peut être facilement remplacé par l'utilisation duarguments
mot - clé.la source
Object.observe()
et aArray.observe()
été retiré de la spécification. L'assistance a déjà été retirée de Chrome. : /Je ne sais pas si cela couvre absolument tout, mais j'utilise quelque chose comme ça (surtout lors du débogage) pour détecter lorsqu'un tableau a un élément ajouté:
la source
Une bibliothèque de collection intéressante est https://github.com/mgesmundo/smart-collection . Vous permet de regarder des tableaux et d'y ajouter des vues. Je ne suis pas sûr de la performance car je la teste moi-même. Mettra à jour ce message bientôt.
la source
J'ai bidouillé et j'ai trouvé ça. L'idée est que l'objet a toutes les méthodes Array.prototype définies, mais les exécute sur un objet tableau séparé. Cela donne la possibilité d'observer des méthodes comme shift (), pop () etc. Bien que certaines méthodes comme concat () ne retournent pas l'objet OArray. La surcharge de ces méthodes ne rendra pas l'objet observable si des accesseurs sont utilisés. Pour réaliser ce dernier, les accesseurs sont définis pour chaque index dans une capacité donnée.
En termes de performances ... OArray est environ 10 à 25 fois plus lent que l'objet Array ordinaire. Pour la capacité dans une plage de 1 à 100, la différence est de 1x-3x.
la source
Je ne vous recommanderais pas d'étendre les prototypes natifs. Au lieu de cela, vous pouvez utiliser une bibliothèque comme new-list; https://github.com/azer/new-list
Il crée un tableau JavaScript natif et vous permet de vous abonner à tout changement. Il regroupe les mises à jour et vous donne la différence finale;
la source