web-dev-qa-db-ja.com

JavaScriptで[]演算子をどのようにオーバーロードしますか

Javascriptで[]演算子をオーバーロードする方法が見つからないようです。誰もが知っていますか?

私は線で考えていました...

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

または私は正しいものを見ていませんか。

70
slyprid

JavaScriptで演算子をオーバーロードすることはできません。

ECMAScript 4の提案 でしたが、拒否されました。

私はあなたがいつでもすぐにそれを見るとは思わない。

73
Peter Bailey

ES6プロキシでこれを行うことができます( all 最新のブラウザーで利用可能)

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

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

[〜#〜] mdn [〜#〜] の詳細を確認してください。

53
average Joe

簡単な答えは、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
15

プロキシを使用します。それは答えのどこかに言及されていましたが、これはより良い例だと思います:

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.
8
Eyal

括弧演算子は実際にはプロパティアクセス演算子であるため、ゲッターとセッターでフックできます。 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要素に対しては機能しますが、他のオブジェクトに対しては機能しません。

このメソッドの欠点は、定義済みの名前のない任意のプロパティではなく、定義済みのプロパティセットへのリクエストのみをフックできることです。

7
kstep

そのため、var whatever = MyClassInstance [4];のようなことを望んでいます。 ?その場合、簡単な答えは、Javascriptは現在、演算子のオーバーロードをサポートしていないということです。

5
Matt Molnar

説明したようにプロキシを使用する必要がありますが、最終的にクラスに統合することができますconstructor

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

これとともに'。次に、setおよびget(deletePropertyも)関数が起動します。異なるように見えるProxyオブジェクトを取得しますが、大部分は、クラスタイプなどの比較(target.constructor === MyClass)を要求するために動作します(たとえtarget.constructor.nameがテキスト(動作が少し異なるものの例を示すだけです。)]

5
Master James

これを行う卑劣な方法の1つは、言語自体を拡張することです。

ステップ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));
4
Dmitry

プロキシすることができますget| setメソッドを直接。 this に触発されました。

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]
0
Jaber