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


85

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

私は次のことを考えていました...

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

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


3
ここでの答えは間違っています。JSの配列は、キーがuint32(-1)値に強制可能であり、プロトタイプに追加のメソッドがあるオブジェクトです
Benjamin Gruenbaum

ちょうどあなたの作るMyClassオブジェクトの配列。あなたはからキーと値をコピーすることができmyArray、あなたのvar myObj = new MyClass()オブジェクト。
jpaugh 2017

ねえ、{}演算子をオーバーロードしたいのですが、何か考えはありますか?
ダニエルアッセイag

回答:


81

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

それはされたのECMAScript 4のために提案したが、拒否されました。

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


4
これは、一部のブラウザのプロキシですでに実行可能であり、ある時点ですべてのブラウザに適用される予定です。github.com/DavidBruant/ProxyArray
Tristan

では、[]と.eq()のどちらを使用するかに応じて、jQueryはどのように異なるものを返すのでしょうか。stackoverflow.com/a/6993901/829305
Rikki

2
あなたは今それをプロキシで行うことができます。
エヤル

ただし、「。」ではなく配列としてアクセスする限り、シンボルを含むメソッドを定義できます。それはSmalltalkのは、のようなものをマッピングする方法だObject arg1: a arg2: b arg3: cとしてObject["arg1:arg2:arg3:"](a,b,c)。だからあなたはmyObject["[]"](1024):P
Dmitry

リンクが
切れています:

69

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

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

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

MDNの詳細を確認してください。


2
これをどのように使用して、インデックスアクセサーを使用して独自のクラスを作成しますか?つまり、独自のコンストラクターを使用したいのですが、プロキシーを作成したくありません。
mpen 2016

1
これは本当のオーバーロードではありません。オブジェクト自体のメソッドを呼び出す代わりに、プロキシのメソッドを呼び出すようになりました。
Pacerier 2017年

target[name]ゲッターで返すことができる@ Pacerier 、OPは例を示しています
Valen

1
連携[]オペレータだけでなく、ところで:var key = 'world'; console.log(proxy[key]);
Klesun

15

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

2
パフォーマンス上の理由からこれは絶対にお勧めしませんが、ここでの問題に対する実際の解決策はこれだけです。おそらく、それが不可能であると述べている編集は、これを素晴らしい答えにするでしょう。
ミリメトリック2012

4
これ質問が望んでいることではありません。問題はfoo['random']、コードが実行できないものをキャッチする方法を求めることです。
Pacerier 2017年

9

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

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.

おそらく、プロキシはES6機能であるため、ブラウザのサポートが制限されていることを言及する価値があります(Babelもそれらを偽造することはできません)。
jdehesa 2017年

8

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

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


jsfiddle.netでアプローチのデモをお願いします。ソリューションは式のどのキーでも機能するはずですobj['any_key'] = 123;が、コードに表示されているものは、(まだ知られていない)キーのセッター/ゲッターを定義する必要があります。それは不可能。
dma_k 2013年

3
これはIEだけではないため、マイナス1をオフセットするプラス1。
オーブ2015

これはクラス関数に対して実行できますか?そのための構文を自分で見つけるのに苦労しています。
Michael Hoffmann

7

説明されているようにプロキシを使用する必要がありますが、最終的にはクラスコンストラクタに統合できます

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

これとともに'。次に、set関数とget(またdeleteProperty)関数が起動します。異なるように見えるProxyオブジェクトを取得しますが、ほとんどの場合、比較を要求するように機能します(target.constructor === MyClass)。クラスタイプなどです。[target.constructor.nameがクラス名である関数ですがテキスト(わずかに異なる動作の例に注意してください。)]


1
これは、バックボーンコレクションの[]をオーバーロードして、他のすべてのプロパティのパススルーとともに[]を使用すると、個々のモデルオブジェクトが返されるようにする場合に適しています。
justkt 2017年

5

したがって、var what = MyClassInstance [4]のようなことをしたいと思っています。?もしそうなら、簡単な答えは、Javascriptは現在演算子のオーバーロードをサポートしていないということです。


1
では、jQueryはどのように機能しますか。$( '。foo')。html()のようなjQueryオブジェクトのメソッドを呼び出すか、$( '
。foo

1
jQueryは関数であり、$関数にパラメーターを渡します。したがって、[]ではなく()括弧
James Westgate

5

これを行うための卑劣な方法の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));

1
あなたが+1曲がっている
強行

絶対に嫌です。大好きです。
SliceThePi

賢さは通常、何らかの形であります。十分なリソースで成果を上げることができる価値のある運動ではないという意味ではありません。最も怠惰な人々は、彼らが利用できない場合でも、彼らがより身近な環境で働くことができるように、彼ら自身のコンパイラーとトランスレーターを書く人々です。とは言うものの、この地域での経験が豊富な人が急いで書いたものであれば、それほど嫌なことではないでしょう。すべての重要な解決策は、何らかの形で嫌なものです。私たちの仕事は、トレードオフを知り、結果に対処することです。
ドミトリー

3

プロキシ取得できます| セットするメソッドを直接ます。これに触発されました

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]
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.