Como você sobrecarregaria o operador no javascript

Eu não consigo encontrar o caminho para 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.

Você não pode sobrecarregar operadores em JavaScript.

Foi proposto para o ECMAScript 4, mas rejeitado.

Eu não acho que você vai ver isso em breve.

Você pode fazer isso com o Proxy ES6 (disponível em todos os navegadores modernos)

 var handler = { get: function(target, name) { return "Hello, " + name; } }; var proxy = new Proxy({}, handler); console.log(proxy.world); // output: Hello, world 

Verifique os detalhes no MDN .

A resposta simples é que o JavaScript permite o access aos filhos de um object através dos colchetes.

Então você pode definir sua class:

 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 class com qualquer syntax.

 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 

Como o operador de colchetes é, na verdade, o operador de access à propriedade, você pode conectá-lo a 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 o IE5-7, existe apenas o evento onpropertychange , que funciona para elementos DOM, mas não para outros objects.

A desvantagem do método é que você só pode ligar as solicitações ao conjunto predefinido de propriedades, não à propriedade arbitrária sem nenhum nome predefinido.

Então você está esperando fazer algo como var whatever = MyClassInstance [4]; ? Se assim for, a resposta simples é que o Javascript não suporta atualmente a sobrecarga do operador.

Use um proxy. 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. 

Você precisa usar Proxy como explicado, mas pode ser integrado em um construtor de class

 return new Proxy(this, { set: function( target, name, value ) { ...}}; 

com isso’. Então as funções set e get (também deleteProperty) serão triggersdas. Embora você obtenha um object Proxy que pareça diferente, na maior parte das vezes ele pede para o compare (target.constructor === MyClass) ser o tipo de class, etc. [embora seja uma function em que target.constructor.name é o nome da class em texto (apenas anotando um exemplo de coisas que funcionam um pouco diferentes.)]

Uma maneira sorrateira de fazer isso é estender a própria linguagem.

passo 1

definir uma convenção de indexação personalizada, vamos chamá-lo, “[]”.

 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)); 

passo 2

definir uma nova implementação eval. (não faça isso dessa maneira, 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.

alternativa:

em vez de recorrer à criação de sua própria linguagem de superconjunto, você pode, em vez disso, compilar sua notação para o idioma existente e, em seguida, avaliá-lo. Isso reduz a sobrecarga de análise para nativa após a primeira vez que você a 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));