Как бы вы перегрузили оператор [] в JavaScript - PullRequest
67 голосов
/ 11 ноября 2009

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

Я думал об этом ...

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

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

Ответы [ 8 ]

71 голосов
/ 11 ноября 2009

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

Было предложено для ECMAScript 4 , но отклонено.

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

51 голосов
/ 04 сентября 2014

Вы можете сделать это с ES6 Proxy (доступно в всех современных браузерах)

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

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

Проверьте детали на MDN .

13 голосов
/ 20 апреля 2011

Простой ответ заключается в том, что 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
7 голосов
/ 21 августа 2012

Поскольку оператор скобок на самом деле является оператором доступа к свойству, вы можете подключить его с помощью методов получения и установки. Для IE вам придется использовать Object.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, но не для других объектов.

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

6 голосов
/ 05 мая 2017

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

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.
5 голосов
/ 27 сентября 2017

Вам нужно использовать Прокси, как объяснено, но в конечном итоге его можно интегрировать в класс Конструктор

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

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

5 голосов
/ 11 ноября 2009

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

2 голосов
/ 24 июля 2017

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

шаг 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));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...