web-dev-qa-db-ja.com

Javascriptオブジェクトのプロパティ値変更のリスナー

Javascriptのドキュメントを調べてみると、Javascriptオブジェクトの次の2つの関数が面白そうだとわかりました。

.watch-プロパティに値が割り当てられるのを監視し、それが発生すると関数を実行します。
.unwatch- watchメソッドで設定されたウォッチポイントを削除します。

使用例:

o = { p: 1 };
o.watch("p", function (id,oldval,newval) {
    console.log("o." + id + " changed from " + oldval + " to " + newval)
    return newval;
});

「p」のプロパティ値を変更するたびに、この関数がトリガーされます。

o.p = 2;   //logs: "o.p changed from 1 to 2"

私は過去数年間Javascriptに取り組んでおり、これらの関数を使用したことはありません。
誰かがこれらの機能が役立ついくつかの良いユースケースを投げてくれませんか?

21
Naga Kiran

時計が実際に設計されているのは、プロパティ値の検証です。たとえば、何かが整数であることを検証できます。

obj.watch('count', function(id, oldval, newval) {
    var val = parseInt(newval, 10);
    if(isNaN(val)) return oldval;
    return val;
});

これを使用して、文字列の長さを検証できます。

obj.watch('name', function(id, oldval, newval) {
    return newval.substr(0, 20);
});

ただし、これらはSpiderMonkey javascriptエンジンの最新バージョンでのみ使用できます。 Jaxerを使用している場合、またはSpiderMonkeyエンジンを組み込んでいる場合に最適ですが、ブラウザーではまだ実際には使用できません(FF3を使用している場合を除く) 。

9
Prestaul

今は2018年で、この質問への回答は少し時代遅れです。

  • Object.watchObject.observe はどちらも非推奨であり、使用しないでください。
  • onPropertyChange は、IEの一部のバージョンでのみ機能するDOM要素イベントハンドラーです。
  • Object.defineProperty を使用すると、オブジェクトプロパティを不変にすることができます。これにより、変更の試行を検出できますが、変更はすべてブロックされます。
  • セッターとゲッターの定義 は機能しますが、多くのセットアップコードが必要であり、新しいプロパティを削除または作成する必要がある場合はうまく機能しません。

現在、オブジェクトに加えられた変更を監視(および傍受)するために プロキシ オブジェクトを使用できるようになりました。これは、OPが行おうとしていることを目的として構築されています。基本的な例は次のとおりです。

var targetObj = {};
var targetProxy = new Proxy(targetObj, {
  set: function (target, key, value) {
      console.log(`${key} set to ${value}`);
      target[key] = value;
      return true;
  }
});

targetProxy.hello_world = "test"; // console: 'hello_world set to test'

Proxyオブジェクトの唯一の欠点は次のとおりです。

  1. Proxyオブジェクトは古いブラウザ(IE11など)では使用できず、 polyfillProxy機能を完全に複製できません。
  2. プロキシオブジェクトは、特別なオブジェクト(Dateなど)で常に期待どおりに動作するとは限りません。Proxyオブジェクトは、プレーンオブジェクトまたは配列と組み合わせるのが最適です。

ネストされたオブジェクトに加えられた変更を監視する必要がある場合は、-などの特殊なライブラリを使用する必要があります。 Observable Slim(私が作成したもの)。それはこのように動作します:

var test = {testing:{}};
var p = ObservableSlim.create(test, true, function(changes) {
    console.log(JSON.stringify(changes));
});

p.testing.blah = 42; // console:  [{"type":"add","target":{"blah":42},"property":"blah","newValue":42,"currentPath":"testing.blah",jsonPointer:"/testing/blah","proxy":{"blah":42}}]
20
Elliot B.

チェックアウト - Object.defineProperty および Object.prototype.\__defineGetter__ (または\__defineSetter__)この機能がどこに向かっているのかを確認します。

Object.definePropertyは、すぐにすべての最新のブラウザで利用できるようになるはずです。

8
Sean Hogan

Javascript Propery Events ライブラリをご覧ください。これは、私が最近作成した、いくつかのイベント呼び出し元でObject.definePropertyを拡張する小さなライブラリです。 HTMLオブジェクトのon[event]プロパティのように使用できるいくつかのon[event]プロパティを追加します。また、失敗した場合にonerrorイベントを呼び出す単純な型チェックもあります。

コードを取得すると、次のようになります。

var o = {}
Object.defineProperty(o, "p", {
    value:1,
    writable:true,
    onchange:function(e){
        console.log("o." + e.target + " changed from " + e.previousValue + " to " + e.returnValue);
    }
})
1
E. P. de Haas

SetIntervalを使用できます

Object.prototype.startWatch = function (onWatch) {

    var self = this;

    if (!self.watchTask) {
        self.oldValues = [];

        for (var propName in self) {
            self.oldValues[propName] = self[propName];
        }


        self.watchTask = setInterval(function () {
            for (var propName in self) {
                var propValue = self[propName];
                if (typeof (propValue) != 'function') {


                    var oldValue = self.oldValues[propName];

                    if (propValue != oldValue) {
                        self.oldValues[propName] = propValue;

                        onWatch({ obj: self, propName: propName, oldValue: oldValue, newValue: propValue });

                    }

                }
            }
        }, 1);
    }



}

var o = { a: 1, b: 2 };

o.startWatch(function (e) {
    console.log("property changed: " + e.propName);
    console.log("old value: " + e.oldValue);
    console.log("new value: " + e.newValue);
});
0
Taner

Object.defineProperty

約束

promiseを削除し、ターゲットブラウザでPromiseがサポートされていない場合にのみコールバックを保持します

重要:

1)promiseを使用する際の非同期動作に注意してください。

2)Object.definePropertyはコールバックをトリガーせず、演算子 '='を割り当てるだけです。

Object.onPropertySet = function onPropertySet(obj, prop, ...callback_or_once){
    let callback, once;
    for(let arg of callback_or_once){
        switch(typeof arg){
        case "function": callback = arg; break;
        case "boolean": once = arg; break;
        }
    }


    let inner_value = obj[prop];
    let p = new Promise(resolve => Object.defineProperty(obj, prop, {
        configurable: true,
        // enumerable: true,
        get(){ return inner_value; },
        set(v){
            inner_value = v;
            if(once){
                Object.defineProperty(obj, prop, {
                    configurable: true,
                    // enumerable: true,
                    value: v,
                    writable: true,
                });
            }
            (callback || resolve)(v);
        }
    }));
    if(!callback) return p;
};

// usage
let a = {};
function sayHiValue(v){ console.log(`Hi "${v}"`); return v; }

// do
Object.onPropertySet(a, "b", sayHiValue);
a.b = 2; // Hi "2"
a.b = 5; // Hi "5"

// or
Object.onPropertySet(a, "c", true).then(sayHiValue).then(v => {
    console.log(a.c); // 4 // because a.c is set immediatly after a.c = 3
    console.log(v); // 3 // very important: v != a.c if a.c is reassigned immediatly
    a.c = 2; // property "c" of object "a" is re-assignable by '=' operator
    console.log(a.c === 2); // true
});
a.c = 3; // Hi "3"
a.c = 4; // (Nothing)
0
Valen