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)
dalam prototipe Array dan melakukan sesuatu seperti antisanity mengatakanDari membaca semua jawaban di sini, saya telah mengumpulkan solusi sederhana yang tidak memerlukan perpustakaan eksternal.
Ini juga menggambarkan dengan lebih baik gagasan umum untuk pendekatan tersebut:
sumber
push
mengembalikanlength
array. Jadi, Anda bisa mendapatkan nilai yang dikembalikan olehArray.prototype.push.apply
ke variabel dan mengembalikannya daripush
fungsi khusus .Saya menemukan yang berikut ini yang tampaknya mencapai ini: https://github.com/mennovanslooten/Observable-Arrays
Observable-Arays memanjang garis bawah dan dapat digunakan sebagai berikut: (dari halaman itu)
sumber
arr[2] = "foo"
, notifikasi perubahannya tidak sinkron . Karena JS tidak menyediakan cara apa pun untuk melihat perubahan tersebut, pustaka ini bergantung pada waktu tunggu yang berjalan setiap 250 md dan memeriksa untuk melihat apakah larik telah berubah sama sekali - jadi Anda tidak akan mendapatkan pemberitahuan perubahan sampai berikutnya. waktu waktu habis. Namun, perubahan lain sepertipush()
mendapat pemberitahuan segera (serentak).Saya menggunakan kode berikut untuk mendengarkan perubahan pada array.
Semoga bermanfaat :)
sumber
Solusi metode Override push yang paling banyak disukai oleh @canon memiliki beberapa efek samping yang tidak nyaman dalam kasus saya:
Itu membuat deskriptor properti push berbeda (
writable
danconfigurable
harus diseteltrue
sebagai gantifalse
), yang menyebabkan pengecualian di kemudian hari.Itu memunculkan acara beberapa kali ketika
push()
dipanggil sekali dengan banyak argumen (sepertimyArray.push("a", "b")
), yang dalam kasus saya tidak perlu dan buruk untuk kinerja.Jadi ini adalah solusi terbaik yang dapat saya temukan yang memperbaiki masalah sebelumnya dan menurut saya lebih bersih / sederhana / lebih mudah dipahami.
Silakan lihat komentar untuk sumber saya dan untuk petunjuk tentang cara mengimplementasikan fungsi mutasi lainnya selain push: 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'.
sumber
...
sintaks yang mirip , dan yang dapat dengan mudah diganti dengan penggunaanarguments
kata kunci.sumber
Object.observe()
danArray.observe()
telah ditarik dari spesifikasi. Dukungan telah ditarik dari Chrome. : /Tidak yakin apakah ini benar-benar mencakup semuanya, tetapi saya menggunakan sesuatu seperti ini (terutama saat debugging) untuk mendeteksi ketika sebuah array memiliki elemen yang ditambahkan:
sumber
Perpustakaan koleksi yang menarik adalah https://github.com/mgesmundo/smart-collection . Memungkinkan Anda untuk menonton array dan menambahkan tampilan padanya juga. Tidak yakin dengan kinerjanya saat saya mengujinya sendiri. Akan segera memperbarui posting ini.
sumber
Saya bermain-main dan menemukan ini. Idenya adalah bahwa objek tersebut memiliki semua metode Array.prototype yang ditentukan, tetapi mengeksekusinya pada objek array yang terpisah. Ini memberikan kemampuan untuk mengamati metode seperti shift (), pop () dll. Meskipun beberapa metode seperti concat () tidak akan mengembalikan objek OArray. Membebani metode tersebut tidak akan membuat objek dapat diamati jika pengakses digunakan. Untuk mencapai yang terakhir, pengakses ditentukan untuk setiap indeks dalam kapasitas yang diberikan.
Dari segi kinerja ... OArray sekitar 10-25 kali lebih lambat dibandingkan dengan objek Array biasa. Untuk kapasitas pada rentang 1 - 100 selisihnya 1x-3x.
sumber
Saya tidak akan merekomendasikan Anda untuk memperluas prototipe asli. Sebagai gantinya, Anda bisa menggunakan perpustakaan seperti daftar baru; https://github.com/azer/new-list
Ini menciptakan array JavaScript asli dan memungkinkan Anda berlangganan perubahan apa pun. Ini mengumpulkan pembaruan dan memberi Anda perbedaan akhir;
sumber