Não consigo encontrar uma maneira de sobrecarregar o operador [] em javascript. Alguém aí sabe?
Eu estava pensando nas linhas de ...
MyClass.operator.lookup(index)
{
return myArray[index];
}
ou não estou olhando para as coisas certas.
Não consigo encontrar uma maneira de sobrecarregar o operador [] em javascript. Alguém aí sabe?
Eu estava pensando nas linhas de ...
MyClass.operator.lookup(index)
{
return myArray[index];
}
ou não estou olhando para as coisas certas.
MyClass
objeto um array. Você pode copiar as chaves e valores de myArray
para o seu var myObj = new MyClass()
objeto.
Respostas:
Você não pode sobrecarregar operadores em JavaScript.
Foi proposto para ECMAScript 4, mas rejeitado.
Eu não acho que você verá isso tão cedo.
Object arg1: a arg2: b arg3: c
como Object["arg1:arg2:arg3:"](a,b,c)
. Então você pode ter myObject["[]"](1024)
: P
target[name]
no getter, OP está apenas mostrando os exemplos
[]
operador, var key = 'world';
console.log(proxy[key]);
A resposta simples é que o JavaScript permite acesso aos filhos de um objeto por meio dos colchetes.
Então você pode definir sua 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;
};
Você poderá então acessar os membros em qualquer instância de sua classe com qualquer uma das sintaxes.
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 seu código não é capaz de fazer.
Use um proxy. Isso foi mencionado em outro lugar nas respostas, mas acho que este é um exemplo melhor:
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 o operador colchetes é, na verdade, um operador de acesso à propriedade, você pode conectá-lo com getters e setters. Para o IE, você terá que usar Object.defineProperty (). Exemplo:
var obj = {
get attr() { alert("Getter called!"); return 1; },
set attr(value) { alert("Setter called!"); return value; }
};
obj.attr = 123;
O mesmo para o IE8 +:
Object.defineProperty("attr", {
get: function() { alert("Getter called!"); return 1; },
set: function(value) { alert("Setter called!"); return value; }
});
Para IE5-7 há onpropertychange
apenas evento, que funciona para elementos DOM, mas não para outros objetos.
A desvantagem do método é que você só pode conectar solicitações a um conjunto predefinido de propriedades, não a propriedades arbitrárias sem qualquer nome predefinido.
obj['any_key'] = 123;
mas o que vejo em seu código, preciso definir setter / getter para qualquer chave (ainda não conhecida). Isso é impossível.
Você precisa usar Proxy conforme explicado, mas ele pode ser integrado a um construtor de classe
return new Proxy(this, {
set: function( target, name, value ) {
...}};
com isso'. Em seguida, as funções set e get (também deleteProperty) serão acionadas. Embora você obtenha um objeto Proxy que parece diferente, na maioria das vezes funciona perguntar a comparação (target.constructor === MyClass) é o tipo de classe etc. [mesmo que seja uma função onde target.constructor.name é o nome da classe em texto (apenas observando um exemplo de coisas que funcionam ligeiramente diferentes.)]
Então, você espera fazer algo como var what = MyClassInstance [4]; ? Em caso afirmativo, a resposta simples é que o Javascript não suporta atualmente a sobrecarga do operador.
uma maneira sorrateira de fazer isso é estendendo a própria linguagem.
definir uma convenção de indexação customizada, vamos chamá-la de "[]".
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 uma nova implementação de avaliação. (não faça assim, mas é uma prova de conceito).
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]");
o acima não funcionará para índices mais complexos, mas pode ser com uma análise mais forte.
em vez de recorrer à criação de sua própria linguagem de superconjunto, você pode compilar sua notação para a linguagem existente e, em seguida, avaliá-la. Isso reduz a sobrecarga de análise para nativa após a primeira vez que você o 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 obter proxy | definir métodos diretamente. Inspirado por isso .
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]