Getter / setter su javascript array?

C’è un modo per ottenere un comportamento get / set su un array? Immagino qualcosa del genere:

var arr = ['one', 'two', 'three']; var _arr = new Array(); for (var i = 0; i < arr.length; i++) { arr[i].__defineGetter__('value', function (index) { //Do something return _arr[index]; }); arr[i].__defineSetter__('value', function (index, val) { //Do something _arr[index] = val; }); } 

L’accesso alla matrice non è diverso dal normale accesso alle proprietà. array[0] significa array['0'] , quindi puoi definire una proprietà con il nome '0' e intercettare l’accesso al primo elemento dell’array attraverso quello.

Tuttavia, ciò lo rende poco pratico per tutti gli array tranne quelli brevi, più o meno fissi. Non è ansible definire una proprietà per “tutti i nomi che capita di essere interi” tutto in una volta.

Usando Proxy , puoi ottenere il comportamento desiderato:

 var _arr = ['one', 'two', 'three']; var accessCount = 0; function doSomething() { accessCount++; } var arr = new Proxy(_arr, { get: function(target, name) { doSomething(); return target[name]; } }); function print(value) { document.querySelector('pre').textContent += value + '\n'; } print(accessCount); // 0 print(arr[0]); // 'one' print(arr[1]); // 'two' print(accessCount); // 2 print(arr.length); // 3 print(accessCount); // 3 print(arr.constructor); // 'function Array() { [native code] }' 
 

Ho consultato l’articolo di John Resig JavaScript Getters And Setter , ma il suo prototipo di esempio non ha funzionato per me. Dopo aver provato alcune alternative, ne ho trovato uno che sembrava funzionare. È ansible utilizzare Array.prototype.__defineGetter__ nel seguente modo:

 Array.prototype.__defineGetter__("sum", function sum(){ var r = 0, a = this, i = a.length - 1; do { r += a[i]; i -= 1; } while (i >= 0); return r; }); var asdf = [1, 2, 3, 4]; asdf.sum; //returns 10 

Ha funzionato per me in Chrome e Firefox.

Spero possa essere d’aiuto.

 Object.extend(Array.prototype, { _each: function(iterator) { for (var i = 0; i < this.length; i++) iterator(this[i]); }, clear: function() { this.length = 0; return this; }, first: function() { return this[0]; }, last: function() { return this[this.length - 1]; }, compact: function() { return this.select(function(value) { return value != undefined || value != null; } ); }, flatten: function() { return this.inject([], function(array, value) { return array.concat(value.constructor == Array ? value.flatten() : [value]); } ); }, without: function() { var values = $A(arguments); return this.select(function(value) { return !values.include(value); } ); }, indexOf: function(object) { for (var i = 0; i < this.length; i++) if (this[i] == object) return i; return -1; }, reverse: function(inline) { return (inline !== false ? this : this.toArray())._reverse(); }, shift: function() { var result = this[0]; for (var i = 0; i < this.length - 1; i++) this[i] = this[i + 1]; this.length--; return result; }, inspect: function() { return '[' + this.map(Object.inspect).join(', ') + ']'; } } ); 

È ansible definire getter e setter per gli array JavaScript. Ma non puoi avere accessor e valori allo stesso tempo. Vedi la documentazione di Mozilla:

Non è ansible simultaneamente avere un getter legato a una proprietà e avere quella proprietà in realtà contiene un valore

Pertanto, se si definiscono i metodi di accesso per un array, è necessario disporre di un secondo array per il valore effettivo. Il seguente esempio lo illustra.

 // // Poor man's prepare for querySelector. // // Example: // var query = prepare ('#modeler table[data-id=?] tr[data-id=?]'); // query[0] = entity; // query[1] = attribute; // var src = document.querySelector(query); // var prepare; { let r = /^([^?]+)\?(.+)$/; // Regular expression to split the query prepare = function (query, base) { if (!base) base = document; var q = []; // List of query fragments var qi = 0; // Query fragment index var v = []; // List of values var vi = 0; // Value index var a = []; // Array containing setters and getters var m; // Regular expression match while (query) { m = r.exec (query); if (m && m[2]) { q[qi++] = m[1]; query = m[2]; (function (qi, vi) { Object.defineProperty (a, vi, { get: function() { return v[vi]; }, set: function(val) { v[vi] = val; q[qi] = JSON.stringify(val); }}); })(qi++, vi++); } else { q[qi++] = query; query = null; } } a.toString = function () { return q.join(''); } return a; } } 

Il codice utilizza tre array:

  1. uno per i valori attuali,
  2. uno per i valori codificati JSON
  3. e uno per gli accessori.

La matrice con gli accessori viene restituita al chiamante. Quando un set viene chiamato assegnando un valore all’elemento array, gli array contenenti i valori plain e codificati vengono aggiornati. Quando viene chiamato, restituisce solo il valore normale. E toString restituisce l’intera query contenente i valori codificati.

Ma come altri hanno già affermato: questo ha senso solo quando la dimensione dell’array è costante. È ansible modificare gli elementi esistenti dell’array ma non è ansible aggiungere ulteriori elementi.

Perché non creare una nuova class per gli oggetti interni?

 var a = new Car(); function Car() { // here create the setters or getters necessary } 

E poi,

 arr = new Array[a, new Car()] 

Penso che tu abbia l’idea.

È ansible creare setter per ogni elemento di un array, ma esiste una limitazione: non sarebbe ansible impostare direttamente gli elementi dell’array per gli indici che si trovano all’esterno dell’area inizializzata (ad esempio, myArray[2] = ... // wouldn't work if myArray.length < 2 ) Utilizzando le funzioni Array.prototype funzionerà. (ad es. spingere, schioccare, unire, spostare, unshift.) Diamo un esempio di come realizzare questo qui .

È ansible aggiungere qualsiasi metodo che si desidera ad una Array , aggiungendoli a Array.prototype . Ecco un esempio che aggiunge un getter e setter

 Array.prototype.get = function(index) { return this[index]; } Array.prototype.set = function(index, value) { this[index] = value; } 

questo è il modo in cui faccio le cose. Dovrai modificare la creazione del prototipo (ho rimosso un po ‘dalla mia versione). Ma questo ti darà il comportamento getter / setter predefinito a cui sono abituato in altre lingue basate su classi. Definire un Getter e nessun Setter significa che la scrittura sull’elemento verrà ignorata …

Spero che questo ti aiuti.

 function Game () { var that = this; this._levels = [[1,2,3],[2,3,4],[4,5,6]]; var self = { levels: [], get levels () { return that._levels; }, setLevels: function(what) { that._levels = what; // do stuff here with // that._levels } }; Object.freeze(self.levels); return self; } 

Questo mi dà il comportamento previsto di:

 var g = new Game() g.levels /// --> [[1,2,3],[2,3,4],[4,5,6]] g.levels[0] /// --> [1,2,3] 

Raccogliendo le critiche di dmvaldman: Scrivere ora dovrebbe essere imansible. Ho riscritto il codice per 1) non utilizzare elementi depracati (__ defineGetter __) e 2) non accettare alcuna scrittura (ovvero: scrittura incontrollata) all’elemento livelli. Un setter di esempio è incluso. (Ho dovuto aggiungere spaziatura a __ defineGetter a causa del markdown)

Dalla richiesta di dmvaldmans:

 g.levels[0] = [2,3,4]; g.levels; /// --> [[1,2,3],[2,3,4],[4,5,6]] //using setter g.setLevels([g.levels, g.levels, 1,2,3,[9]]); g.levels; /// --> [[[1,2,3],[2,3,4],[4,5,6]],[[1,2,3],[2,3,4],[4,5,6]], ....] //using setLevels g.setLevels([2,3,4]); g.levels; /// --> [2,3,4] 

Questa risposta è solo un’estensione della soluzione basata su Proxy. Vedi la soluzione con proxy, in quanto solo viene menzionato get, ma possiamo anche usare set come sto mostrando qui.

Avviso: il terzo argomento nel set può portare il valore …

Il codice è auto esplicativo.

 var _arr = ['one', 'two', 'three']; var accessCount = 0; function doSomething() { accessCount++; } var arr = new Proxy(_arr, { get: function(target, name) { doSomething(); return target[name]; }, set: function(target, name, val) { doSomething(); target[name] = val; } }); function print(value) { document.querySelector('pre').textContent += value + '\n'; } print(accessCount); // 0 print(arr[0]); // 'one' print(accessCount); // 1 arr[1] = 10; print(accessCount); // 2 print(arr[1]); // 10