1. ホーム
  2. javascript

[解決済み] JavaScriptで動的なゲッター/セッターを実装することは可能ですか?

2022-04-25 01:01:49

質問

以下のような方法で、すでに名前がわかっているプロパティに対してゲッターとセッターを作成する方法を知っています。

// A trivial example:
function MyObject(val){
    this.count = 0;
    this.value = val;
}
MyObject.prototype = {
    get value(){
        return this.count < 2 ? "Go away" : this._value;
    },
    set value(val){
        this._value = val + (++this.count);
    }
};
var a = new MyObject('foo');

alert(a.value); // --> "Go away"
a.value = 'bar';
alert(a.value); // --> "bar2"

さて、質問ですが、このようなキャッチオールなゲッターとセッターを定義することは可能でしょうか? つまり、以下のようなプロパティ名に対して、ゲッターとセッターを作成することができます。 でない場合 すでに定義されています。

このコンセプトは、PHPで __get()__set() マジックメソッド ( PHPのドキュメント ということで、これに相当するJavaScriptはないのでしょうか?

もちろん、クロスブラウザに対応したソリューションが理想的なのは言うまでもありません。

どのように解決するのですか?

ES2015 (別名: ES6") 仕様から変更されました。JavaScriptは現在 プロキシ . プロキシは、他のオブジェクトの真のプロキシ(ファサード)となるオブジェクトを作成することができます。以下は、文字列のプロパティ値をすべて大文字に変換して返す簡単な例です。 "missing" の代わりに undefined のように、存在しないプロパティに対して

"use strict";
if (typeof Proxy == "undefined") {
    throw new Error("This browser doesn't support Proxy");
}
let original = {
    example: "value",
};
let proxy = new Proxy(original, {
    get(target, name, receiver) {
        if (Reflect.has(target, name)) {
            let rv = Reflect.get(target, name, receiver);
            if (typeof rv === "string") {
                rv = rv.toUpperCase();
            }
            return rv;
        }
        return "missing";
      }
});
console.log(`original.example = ${original.example}`); // "original.example = value"
console.log(`proxy.example = ${proxy.example}`);       // "proxy.example = VALUE"
console.log(`proxy.unknown = ${proxy.unknown}`);       // "proxy.unknown = missing"
original.example = "updated";
console.log(`original.example = ${original.example}`); // "original.example = updated"
console.log(`proxy.example = ${proxy.example}`);       // "proxy.example = UPDATED"

オーバーライドしない操作は、そのデフォルトの動作になります。上記の例では、オーバーライドするのは get しかし、あなたがフックすることができる操作の全リストがあります。

において get ハンドラ関数の引数リストです。

  • target はプロキシされるオブジェクト ( original この例では)。
  • name は(もちろん)取得されるプロパティの名前で、通常は文字列ですが、シンボルでもかまいません。
  • receiver として使用されるべきオブジェクトです。 this は、プロパティがデータプロパティではなくアクセッサである場合、ゲッター関数の中にあります。通常の場合、これはプロキシかそれを継承したものですが、それは できる によってトラップが発生する可能性があるため、何でもありです。 Reflect.get .

これにより、必要なキャッチオールゲッターとセッターの機能を持つオブジェクトを作成することができます。

"use strict";
if (typeof Proxy == "undefined") {
    throw new Error("This browser doesn't support Proxy");
}
let obj = new Proxy({}, {
    get(target, name, receiver) {
        if (!Reflect.has(target, name)) {
            console.log("Getting non-existent property '" + name + "'");
            return undefined;
        }
        return Reflect.get(target, name, receiver);
    },
    set(target, name, value, receiver) {
        if (!Reflect.has(target, name)) {
            console.log(`Setting non-existent property '${name}', initial value: ${value}`);
        }
        return Reflect.set(target, name, value, receiver);
    }
});

console.log(`[before] obj.example = ${obj.example}`);
obj.example = "value";
console.log(`[after] obj.example = ${obj.example}`);

上記のように出力されます。

存在しないプロパティ 'example' の取得
[前] obj.example = undefined
存在しないプロパティ'example'の設定、初期値:value
[後】 obj.example = value

を取得しようとすると、"non-existent" というメッセージが表示されることに注意してください。 example がまだ存在しないとき、そしてそれを作成したとき、それ以後は存在しません。


2011年の回答 (Internet ExplorerのようなES5の機能に制限された環境では、まだ有効です。) :

いいえ、JavaScriptにはプロパティをキャッチオールする機能はありません。あなたが使っているアクセッサ構文は 11.1.5節 は、ワイルドカードやそのようなものを提供しません。

もちろん、それを実現するための関数を実装することもできますが、おそらくは f = obj.prop("example"); よりも f = obj.example;obj.prop("example", value); よりも obj.example = value; (これは、関数が未知のプロパティを処理するために必要です)。

参考までに、ゲッター関数(セッターのロジックは気にしませんでした)は次のようになります。

MyObject.prototype.prop = function(propName) {
    if (propName in this) {
        // This object or its prototype already has this property,
        // return the existing value.
        return this[propName];
    }

    // ...Catch-all, deal with undefined property here...
};

しかし、繰り返しになりますが、オブジェクトの使い方が変わってしまうので、本当にそうしたいとは思えません。