Nie mogę znaleźć sposobu na przeciążenie operatora [] w javascript. Czy ktoś tam wie?
Myślałem na wzór ...
MyClass.operator.lookup(index)
{
return myArray[index];
}
czy też nie patrzę na właściwe rzeczy.
Nie mogę znaleźć sposobu na przeciążenie operatora [] w javascript. Czy ktoś tam wie?
Myślałem na wzór ...
MyClass.operator.lookup(index)
{
return myArray[index];
}
czy też nie patrzę na właściwe rzeczy.
MyClass
obiektu tablicę. Możesz skopiować klucze i wartości z myArray
do swojego var myObj = new MyClass()
obiektu.
Odpowiedzi:
Nie możesz przeciążać operatorów w JavaScript.
Został zaproponowany dla ECMAScript 4, ale został odrzucony.
Myślę, że nie zobaczysz tego w najbliższym czasie.
Object arg1: a arg2: b arg3: c
jak Object["arg1:arg2:arg3:"](a,b,c)
. Możesz więc mieć myObject["[]"](1024)
: P
Możesz to zrobić za pomocą ES6 Proxy (dostępne we wszystkich nowoczesnych przeglądarkach)
var handler = {
get: function(target, name) {
return "Hello, " + name;
}
};
var proxy = new Proxy({}, handler);
console.log(proxy.world); // output: Hello, world
Sprawdź szczegóły na MDN .
target[name]
w getterze , OP pokazuje tylko przykłady
[]
operatorem, przy okazji:var key = 'world';
console.log(proxy[key]);
Prosta odpowiedź jest taka, że JavaScript umożliwia dostęp do elementów potomnych Object poprzez nawiasy kwadratowe.
Możesz więc zdefiniować swoją klasę:
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;
};
Będziesz wtedy mógł uzyskać dostęp do członków we wszystkich wystąpieniach swojej klasy za pomocą dowolnej składni.
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']
którego twój kod nie jest w stanie zrobić.
Użyj proxy. Wspomniano o tym w innym miejscu odpowiedzi, ale myślę, że to jest lepszy przykład:
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.
Ponieważ operator nawiasów jest w rzeczywistości operatorem dostępu do właściwości, można go podłączyć za pomocą metod pobierających i ustawiających. W przypadku IE będziesz musiał zamiast tego użyć Object.defineProperty (). Przykład:
var obj = {
get attr() { alert("Getter called!"); return 1; },
set attr(value) { alert("Setter called!"); return value; }
};
obj.attr = 123;
To samo dla IE8 +:
Object.defineProperty("attr", {
get: function() { alert("Getter called!"); return 1; },
set: function(value) { alert("Setter called!"); return value; }
});
W IE5-7 istnieje onpropertychange
tylko zdarzenie, które działa dla elementów DOM, ale nie dla innych obiektów.
Wadą tej metody jest to, że można podpiąć żądania tylko do wstępnie zdefiniowanego zestawu właściwości, a nie do dowolnej właściwości bez wcześniej zdefiniowanej nazwy.
obj['any_key'] = 123;
ale to, co widzę w Twoim kodzie, muszę zdefiniować setter / getter dla dowolnego (jeszcze nieznanego) klucza. To jest niemożliwe.
Musisz użyć Proxy, jak wyjaśniono, ale ostatecznie można go zintegrować z konstruktorem klasy
return new Proxy(this, {
set: function( target, name, value ) {
...}};
z tym'. Następnie uruchomione zostaną funkcje set i get (także deleteProperty). Chociaż otrzymujesz obiekt Proxy, który wydaje się inny, w większości przypadków działa on w celu zapytania porównania (target.constructor === MyClass) o typ klasy itd. [Nawet jeśli jest to funkcja, w której target.constructor.name jest nazwą klasy w tekst (wystarczy podać przykład rzeczy, które działają nieco inaczej)]
Więc masz nadzieję zrobić coś takiego jak var cokolwiek = MyClassInstance [4]; ? Jeśli tak, prostą odpowiedzią jest to, że JavaScript nie obsługuje obecnie przeciążania operatorów.
jednym z podstępnych sposobów jest rozszerzenie samego języka.
zdefiniuj niestandardową konwencję indeksowania, nazwijmy ją „[]”.
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));
zdefiniować nową implementację oceny. (nie rób tego w ten sposób, ale jest to dowód słuszności koncepcji).
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]");
powyższe nie zadziała dla bardziej złożonych indeksów, ale może być przy silniejszym parsowaniu.
zamiast uciekać się do tworzenia własnego supersetu języka, możesz zamiast tego skompilować swoją notację do istniejącego języka, a następnie ją ewaluować. Zmniejsza to obciążenie parsowania do natywnego po pierwszym użyciu.
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));
Możemy proxy GET | ustaw metody bezpośrednio. Zainspirowany tym .
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]