Getter / setter su JavaScript array?
-
20-09-2019 - |
Domanda
C'è un modo per ottenere un get / set comportamento su un array? Immagino qualcosa di simile:
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;
});
}
Soluzione
Array accesso non è diverso alla normalità l'accesso alle proprietà. array[0]
significa array['0']
, in modo da poter definire una proprietà con il nome '0'
e accesso intercettare al primo elemento dell'array attraverso quella.
Tuttavia, ciò lo rende poco pratico per tutti, ma le matrici breve, più o meno a lunghezza fissa. Non è possibile definire una proprietà per “tutti i nomi che si trovano ad essere numeri interi” tutti in una volta.
Altri suggerimenti
Proxy , è possibile 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] }'
<pre></pre>
Il costruttore Proxy creerà un oggetto avvolgendo le nostre funzioni di array e utilizzare chiamati trappole per ignorare i comportamenti di base. La funzione get
sarà chiamata per qualsiasi di ricerca immobili, e doSomething()
prima di restituire il valore.
proxy sono una caratteristica ES6 e non sono supportati in IE11 o inferiore. Vedere lista di compatibilità del browser.
Ho guardato in di John Resig articolo JavaScript getter e setter , ma il suo esempio prototipo non ha funzionato per me. Dopo aver provato alcune alternative, ho trovato uno che sembrava funzionare. È possibile 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 lavorato per me in Chrome e Firefox.
Spero che aiuta.
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(', ') + ']';
}
}
);
E 'possibile definire getter e setter per gli array JavaScript. Ma non si può avere funzioni di accesso e di valori allo stesso tempo. Vedere la Mozilla documentazione :
Non è possibile avere contemporaneamente un getter legata a una proprietà e che hanno proprietà in realtà contenere un valore
Quindi, se si definisce di accesso per una matrice è necessario disporre di un secondo array per il valore effettivo. I seguenti 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 matrici:
- una per i valori reali,
- una per i valori codificati JSON
- e uno per le funzioni di accesso.
L'array con le funzioni di accesso viene restituito al chiamante. Quando un set
viene chiamato assegnando un valore per l'elemento di matrice, le matrici contenenti i valori normali e codificati vengono aggiornati. Quando get
viene chiamato, restituisce solo il valore normale. E toString
restituisce l'intera query contenente i valori codificati.
Ma come altri hanno detto già: questo senso solo, quando la dimensione della matrice è costante. È possibile modificare gli elementi esistenti della matrice, ma non è possibile aggiungere ulteriori elementi.
Perché non creare una nuova classe 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 si ottiene l'idea.
È possibile creare incastonatori per ciascun elemento di un array, ma c'è una limitazione: non sarebbe in grado di impostare direttamente elementi dell'array di indici che sono al di fuori della regione inizializzato (es myArray[2] = ... // wouldn't work if myArray.length < 2
) Utilizzando le funzioni Array.prototype funzionerà. (Es spinta, pop, splice, shift, unshift.) Mi danno un esempio di come realizzare questo qui .
È possibile aggiungere qualsiasi metodo che ti piace ad un Array
, con l'aggiunta di loro di 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 mio modo di fare le cose. Si dovrà modificare la Creazione Prototype (ho tolto un po 'dalla mia versione). Ma questo vi darà il comportamento predefinito getter / setter sono abituato a in altra classe-Based Lingue. Definizione di un Getter e nessun Setter significa che la scrittura per l'elemento viene ignorato ...
Spero che questo 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:
var g = new Game()
g.levels
/// --> [[1,2,3],[2,3,4],[4,5,6]]
g.levels[0]
/// --> [1,2,3]
Riprendendo il critizism da dmvaldman: La scrittura dovrebbe essere impossibile. Ho riscritto il codice per 1) Non utilizzare elementi depracated (__ defineGetter __) e 2), non accettare qualsiasi iscritto (cioè: la scrittura non controllata) all'elemento livelli. Un esempio setter è incluso. (Ho dovuto aggiungere spaziatura a __ defineGetter a causa della riduzione dei prezzi)
Da Richiesta 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. Vedere la soluzione con delega, in quanto solo ottiene è menzionato, ma possiamo anche usare impostato come sto mostrando qui.
Avviso: 3 ° argomento nel set può portare il valore ...
Il codice si spiega da sé.
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