Non riesco a trovare il modo per sovraccaricare l'operatore [] in javascript. Qualcuno là fuori lo sa?
Stavo pensando sulla falsariga di ...
MyClass.operator.lookup(index)
{
return myArray[index];
}
o non sto guardando le cose giuste.
Non riesco a trovare il modo per sovraccaricare l'operatore [] in javascript. Qualcuno là fuori lo sa?
Stavo pensando sulla falsariga di ...
MyClass.operator.lookup(index)
{
return myArray[index];
}
o non sto guardando le cose giuste.
MyClass
oggetto un array. Puoi copiare le chiavi e i valori da myArray
nel tuo var myObj = new MyClass()
oggetto.
Risposte:
Non puoi sovraccaricare gli operatori in JavaScript.
È stato proposto per ECMAScript 4 ma rifiutato.
Non credo che lo vedrai presto.
Object arg1: a arg2: b arg3: c
come Object["arg1:arg2:arg3:"](a,b,c)
. Quindi puoi avere myObject["[]"](1024)
: P
target[name]
nel getter, OP mostra solo gli esempi
[]
operatore, tra l'altro:var key = 'world';
console.log(proxy[key]);
La semplice risposta è che JavaScript consente l'accesso ai figli di un oggetto tramite le parentesi quadre.
Quindi potresti definire la tua classe:
MyClass = function(){
// Set some defaults that belong to the class via dot syntax or array syntax.
this.some_property = 'my value is a string';
this['another_property'] = 'i am also a string';
this[0] = 1;
};
Sarai quindi in grado di accedere ai membri su qualsiasi istanza della tua classe con entrambe le sintassi.
foo = new MyClass();
foo.some_property; // Returns 'my value is a string'
foo['some_property']; // Returns 'my value is a string'
foo.another_property; // Returns 'i am also a string'
foo['another_property']; // Also returns 'i am also a string'
foo.0; // Syntax Error
foo[0]; // Returns 1
foo['0']; // Returns 1
foo['random']
che il tuo codice non è in grado di fare.
Usa un proxy. È stato menzionato altrove nelle risposte ma penso che questo sia un esempio migliore:
var handler = {
get: function(target, name) {
if (name in target) {
return target[name];
}
if (name == 'length') {
return Infinity;
}
return name * name;
}
};
var p = new Proxy({}, handler);
p[4]; //returns 16, which is the square of 4.
Poiché l'operatore delle parentesi è in realtà un operatore di accesso alla proprietà, puoi agganciarlo con getter e setter. Per IE dovrai invece usare Object.defineProperty (). Esempio:
var obj = {
get attr() { alert("Getter called!"); return 1; },
set attr(value) { alert("Setter called!"); return value; }
};
obj.attr = 123;
Lo stesso per IE8 +:
Object.defineProperty("attr", {
get: function() { alert("Getter called!"); return 1; },
set: function(value) { alert("Setter called!"); return value; }
});
Per IE5-7 c'è onpropertychange
solo l'evento, che funziona per gli elementi DOM, ma non per altri oggetti.
Lo svantaggio del metodo è che puoi solo agganciare le richieste a un insieme predefinito di proprietà, non su proprietà arbitrarie senza alcun nome predefinito.
obj['any_key'] = 123;
ma quello che vedo nel tuo codice devo definire setter / getter per qualsiasi chiave (non ancora nota). Questo è impossibile.
È necessario utilizzare Proxy come spiegato, ma alla fine può essere integrato in un costruttore di classi
return new Proxy(this, {
set: function( target, name, value ) {
...}};
con questo'. Quindi verranno attivate le funzioni set e get (anche deleteProperty). Anche se ottieni un oggetto Proxy che sembra diverso, per la maggior parte funziona per chiedere il confronto (target.constructor === MyClass) è il tipo di classe ecc. [Anche se è una funzione in cui target.constructor.name è il nome della classe in testo (solo annotando un esempio di cose che funzionano in modo leggermente diverso.)]
Quindi speri di fare qualcosa come var qualunque = MyClassInstance [4]; ? In tal caso, la semplice risposta è che Javascript attualmente non supporta il sovraccarico dell'operatore.
un modo subdolo per farlo è estendere la lingua stessa.
definire una convenzione di indicizzazione personalizzata, chiamiamola "[]".
var MyClass = function MyClass(n) {
this.myArray = Array.from(Array(n).keys()).map(a => 0);
};
Object.defineProperty(MyClass.prototype, "[]", {
value: function(index) {
return this.myArray[index];
}
});
...
var foo = new MyClass(1024);
console.log(foo["[]"](0));
definire una nuova implementazione della valutazione. (non farlo in questo modo, ma è una prova di concetto).
var MyClass = function MyClass(length, defaultValue) {
this.myArray = Array.from(Array(length).keys()).map(a => defaultValue);
};
Object.defineProperty(MyClass.prototype, "[]", {
value: function(index) {
return this.myArray[index];
}
});
var foo = new MyClass(1024, 1337);
console.log(foo["[]"](0));
var mini_eval = function(program) {
var esprima = require("esprima");
var tokens = esprima.tokenize(program);
if (tokens.length == 4) {
var types = tokens.map(a => a.type);
var values = tokens.map(a => a.value);
if (types.join(';').match(/Identifier;Punctuator;[^;]+;Punctuator/)) {
if (values[1] == '[' && values[3] == ']') {
var target = eval(values[0]);
var i = eval(values[2]);
// higher priority than []
if (target.hasOwnProperty('[]')) {
return target['[]'](i);
} else {
return target[i];
}
return eval(values[0])();
} else {
return undefined;
}
} else {
return undefined;
}
} else {
return undefined;
}
};
mini_eval("foo[33]");
quanto sopra non funzionerà per indici più complessi ma può essere con un'analisi più forte.
invece di ricorrere alla creazione della tua lingua superset, puoi invece compilare la tua notazione nella lingua esistente, quindi valutarla. Ciò riduce l'overhead di analisi a nativo dopo la prima volta che lo usi.
var compile = function(program) {
var esprima = require("esprima");
var tokens = esprima.tokenize(program);
if (tokens.length == 4) {
var types = tokens.map(a => a.type);
var values = tokens.map(a => a.value);
if (types.join(';').match(/Identifier;Punctuator;[^;]+;Punctuator/)) {
if (values[1] == '[' && values[3] == ']') {
var target = values[0];
var i = values[2];
// higher priority than []
return `
(${target}['[]'])
? ${target}['[]'](${i})
: ${target}[${i}]`
} else {
return 'undefined';
}
} else {
return 'undefined';
}
} else {
return 'undefined';
}
};
var result = compile("foo[0]");
console.log(result);
console.log(eval(result));
Possiamo ottenere tramite proxy | impostare direttamente i metodi. Ispirato da questo .
class Foo {
constructor(v) {
this.data = v
return new Proxy(this, {
get: (obj, key) => {
if (typeof(key) === 'string' && (Number.isInteger(Number(key)))) // key is an index
return obj.data[key]
else
return obj[key]
},
set: (obj, key, value) => {
if (typeof(key) === 'string' && (Number.isInteger(Number(key)))) // key is an index
return obj.data[key] = value
else
return obj[key] = value
}
})
}
}
var foo = new Foo([])
foo.data = [0, 0, 0]
foo[0] = 1
console.log(foo[0]) // 1
console.log(foo.data) // [1, 0, 0]