Parece que no puedo encontrar la manera de sobrecargar el operador [] en javascript. ¿Alguien lo sabe?
Estaba pensando en las líneas de ...
MyClass.operator.lookup(index)
{
return myArray[index];
}
o no estoy mirando las cosas correctas.
Parece que no puedo encontrar la manera de sobrecargar el operador [] en javascript. ¿Alguien lo sabe?
Estaba pensando en las líneas de ...
MyClass.operator.lookup(index)
{
return myArray[index];
}
o no estoy mirando las cosas correctas.
MyClass
objeto en una matriz. Puede copiar las claves y los valores de myArray
a su var myObj = new MyClass()
objeto.
Respuestas:
No puede sobrecargar operadores en JavaScript.
Se propuso para ECMAScript 4 pero se rechazó.
No creo que lo veas pronto.
Object arg1: a arg2: b arg3: c
como Object["arg1:arg2:arg3:"](a,b,c)
. Entonces puedes tener myObject["[]"](1024)
: P
target[name]
en el getter, OP solo muestra los ejemplos
[]
También funciona con el operador, por cierto:var key = 'world';
console.log(proxy[key]);
La respuesta simple es que JavaScript permite el acceso a los elementos secundarios de un objeto mediante los corchetes.
Entonces podrías definir tu clase:
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;
};
Luego podrá acceder a los miembros en cualquier instancia de su clase con cualquiera de las sintaxis.
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']
que su código no puede hacer.
Utilice un proxy. Se mencionó en otra parte de las respuestas, pero creo que este es un mejor ejemplo:
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.
Como el operador de paréntesis es en realidad un operador de acceso a la propiedad, puede conectarlo con captadores y definidores. Para IE, tendrá que usar Object.defineProperty () en su lugar. Ejemplo:
var obj = {
get attr() { alert("Getter called!"); return 1; },
set attr(value) { alert("Setter called!"); return value; }
};
obj.attr = 123;
Lo mismo para IE8 +:
Object.defineProperty("attr", {
get: function() { alert("Getter called!"); return 1; },
set: function(value) { alert("Setter called!"); return value; }
});
Para IE5-7 solo hay onpropertychange
evento, que funciona para elementos DOM, pero no para otros objetos.
El inconveniente del método es que solo puede enlazar solicitudes a un conjunto predefinido de propiedades, no a una propiedad arbitraria sin un nombre predefinido.
obj['any_key'] = 123;
pero lo que veo en su código, necesito definir setter / getter para cualquier clave (aún no conocida). Eso es imposible.
Necesita usar Proxy como se explica, pero finalmente se puede integrar en un constructor de clases
return new Proxy(this, {
set: function( target, name, value ) {
...}};
con este'. Entonces se activarán las funciones set y get (también deleteProperty). Aunque obtienes un objeto Proxy que parece diferente, en su mayor parte funciona preguntarle al comparar (target.constructor === MyClass) es el tipo de clase, etc. [aunque es una función donde target.constructor.name es el nombre de la clase en texto (solo señalando un ejemplo de cosas que funcionan de manera ligeramente diferente)]
Así que espera hacer algo como var anything = MyClassInstance [4]; ? Si es así, la respuesta simple es que Javascript no admite actualmente la sobrecarga del operador.
una forma engañosa de hacer esto es extendiendo el lenguaje en sí.
definir una convención de indexación personalizada, llamémosla, "[]".
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));
definir una nueva implementación de evaluación. (no hagas esto de esta manera, pero es una prueba de concepto).
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]");
lo anterior no funcionará para índices más complejos, pero puede ser con un análisis más sólido.
en lugar de recurrir a crear su propio lenguaje de superconjunto, puede compilar su notación en el lenguaje existente y luego evaluarlo. Esto reduce la sobrecarga de análisis a nativo después de la primera vez que lo usa.
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));
Podemos obtener proxy | establecer métodos directamente. Inspirado por esto .
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]