Как бы вы перегрузили оператор [] в javascript


Я не могу найти способ перегрузить оператор [] в javascript. Кто-нибудь там знает?

Я думал о строках ...

MyClass.operator.lookup(index)
{
     return myArray[index];
}

или я не смотрю на правильные вещи.

8 63

8 ответов:

вы не можете перегружать операторы в JavaScript.

Он был предлагается для ECMAScript 4 но отверг.

Я не думаю, что вы увидите это в ближайшее время.

вы можете сделать это с помощью прокси ES6 (доступно в все современных браузерах)

var handler = {
    get: function(target, name) {
        return "Hello, " + name;
    }
};
var proxy = new Proxy({}, handler);

console.log(proxy.world); // output: Hello, world

на MDN.

простой ответ заключается в том, что JavaScript позволяет получить доступ к дочерним объектам через квадратные скобки.

таким образом, вы можете определить свой класс:

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

вы сможете получить доступ к членам на любых экземплярах вашего класса с любым синтаксисом.

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

поскольку оператор скобок на самом деле является оператором доступа к свойствам, вы можете подключить его с помощью геттеров и сеттеров. Для IE вам придется использовать объект.defineProperty() вместо этого. Пример:

var obj = {
    get attr() { alert("Getter called!"); return 1; },
    set attr(value) { alert("Setter called!"); return value; }
};

obj.attr = 123;

то же самое для IE8+:

Object.defineProperty("attr", {
    get: function() { alert("Getter called!"); return 1; },
    set: function(value) { alert("Setter called!"); return value; }
});

для IE5-7 есть onpropertychange только событие, которое работает для элементов DOM, но не для других объектов.

недостатком метода является то, что вы можете подключать только запросы к предопределенному набору свойств, а не к произвольному свойству без любое предопределенное имя.

Итак, вы надеетесь сделать что-то вроде var whatever = MyClassInstance[4]; ? Если это так, простой ответ заключается в том, что Javascript в настоящее время не поддерживает перегрузку операторов.

Использовать прокси. Упоминалось в других ответов, но я думаю, что это лучший пример:

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.

вам нужно использовать прокси, как описано, но в конечном итоге он может быть интегрирован в класс конструктор

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

С 'этим'. Затем будут запущены функции set и get (также deleteProperty). Хотя вы получаете прокси-объект, который кажется другим, он по большей части работает, чтобы спросить compare ( target.конструктор === MyClass в ) это тип класса и т. д. [хотя это функция, где target.constructor.name это имя класса в тексте (просто отметив пример вещи немного разные.)]

один хитрый способ сделать это-расширить сам язык.

Шаг 1

определите пользовательское соглашение об индексировании, назовем его" []".

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

Шаг 2

определите новую реализацию eval. (не делайте это таким образом, но это доказательство концепции).

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

выше не будет работать для более сложных индексов, но это может быть с сильной разбора.

альтернатива:

вместо того, чтобы прибегать к создавая свой собственный язык надмножества, вы можете вместо этого скомпилировать свою нотацию на существующий язык, а затем оценить его. Это уменьшает накладные расходы на синтаксический анализ до собственного после первого использования.

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