Object.defineProperty get set返回错误的值

时间:2016-04-13 09:41:52

标签: javascript

假设我有一个这样的对象实例:

var objectA = {"a": 1, "b": 2, "c" : 3};

在我的代码中我像这样访问属性:

cc.log(objectA.a); // output 1

现在我想为这个对象添加一个get / set来提供一些简单的加密/解密功能:

hookSetGet: function (someObject) {
    for (var key in someObject) {
        cc.log("key: " + key);

        // store the origin value before Object.defineProperty
        var pureValue = someObject[key];

        // add a property to store the encrypted value
        var hiddenValueKey = "__" + key;
        someObject[hiddenValueKey] = undefined;

        Object.defineProperty (
            someObject,
            key, 
            {
                set: function (val) {
                    // simulate encrypt
                    this.hiddenValueKey = val + 1;
                    cc.log("hooked set: " + val + " - " + this.hiddenValueKey);
                },
                get: function () {
                    cc.log("hooked get: " + this.hiddenValueKey + " - " + (this.hiddenValueKey - 1));
                    // simulate decrypt
                    return this.hiddenValueKey - 1;
                }
            }
        );

        // trigger set to encrypt
        someObject[key] = pureValue;
    }
}

但是当我测试这样的函数时:

var objectA = {"a": 1, "b": 2, "c" : 3};
this.hookSetGet(objectA);

cc.log(objectA.a);
cc.log(objectA.b);
cc.log(objectA.c);

我没有得到我想要的结果:

key: a
hooked set: 1 - 2
key: b
hooked set: 2 - 3
key: c
hooked set: 3 - 4

hooked get: 4 - 3
3
hooked get: 4 - 3
3
hooked get: 4 - 3
3

即使我打电话

也好像
objectA.a

我会得到

的价值
objectA.c

这个问题似乎很简单,但我无法弄清楚哪里出错了。

任何建议都将不胜感激,谢谢:)

更新:

我尝试了以下代码而不更改hookSetGet的代码:

cc.log(objectA.__a);
cc.log(objectA.__b);
cc.log(objectA.__c);

并获得:

undefined
undefined
undefined

然后我改变了hookSetGet函数:

set: function (val) {
    // simulate encrypt
    someObject[hiddenValueKey] = val + 1;
    cc.log("hooked set: " + val + " - " + someObject[hiddenValueKey]);
},
get: function () {
    cc.log("hooked get: " + someObject[hiddenValueKey] + " - " + (someObject[hiddenValueKey] - 1));
    // simulate decrypt
    return someObject[hiddenValueKey] - 1;
}

我将所有this.hiddenValueKey更改为someObject [hiddenValueKey]。

,输出为:

cc.log(objectA.__a);  // 2   good
cc.log(objectA.__b);  // 3   good
cc.log(objectA.__c);  // 4   good

cc.log(objectA.a);    // hooked get: 4 - 3   still wrong
cc.log(objectA.b);    // hooked get: 4 - 3   still wrong
cc.log(objectA.c);    // hooked get: 4 - 3   still wrong

2 个答案:

答案 0 :(得分:1)

所以,你写了这个:

   Object.defineProperty (
        someObject,
        key, 
        {
            set: function (val) {
                // simulate encrypt
                this.hiddenValueKey = val + 1;
                cc.log("hooked set: " + val + " - " + this.hiddenValueKey);
            },
            get: function () {
                cc.log("hooked get: " + this.hiddenValueKey + " - " + (this.hiddenValueKey - 1));
                // simulate decrypt
                return this.hiddenValueKey - 1;
            }
        }
    );

来自this的getter和setter this.hiddenValueKey在所有情况下都指向您的objectA对象,而不是指每个属性。因此,当您想为每个属性设置一个值时,实际上是在覆盖objectA.hiddenValueKey。这就是为什么当您尝试get返回值时,您只能获得设置的最后一个值。

即使您将hiddenValueKey设置为唯一,在getter和setter中也可以访问相同的属性。这是因为this.hiddenValueKey与撰写this['hiddenValueKey']相同。你的意思是写this[hiddenValueKey]吗?即使你这样做,你可能会遇到一些范围问题,hiddenValueKey在你退出循环后总是拥有最新的键值。

所以,你可以试试这个:

   Object.defineProperty (
        someObject,
        key, 
        {
            set: function (val) {
                // simulate encrypt
                this[hiddenValueKey] = val + 1;
                cc.log("hooked set: " + val + " - " + this[hiddenValueKey]);
            },
            get: function () {
                cc.log("hooked get: " + this[hiddenValueKey] + " - " + (this[hiddenValueKey] - 1));
                // simulate decrypt
                return this[hiddenValueKey] - 1;
            }
        }
    );

但是,正如我所说,你可能必须为hiddenValueKey变量创建一个闭包,因此它对于每个属性getter和setter都是唯一的。

您可以创建一个这样的闭包:

   (function(hiddenValueKey) {
     Object.defineProperty (
        someObject,
        key, 
        {
            set: function (val) {
                // simulate encrypt
                this[hiddenValueKey] = val + 1;
                cc.log("hooked set: " + val + " - " + this[hiddenValueKey]);
            },
            get: function () {
                cc.log("hooked get: " + this[hiddenValueKey] + " - " + (this[hiddenValueKey] - 1));
                // simulate decrypt
                return this[hiddenValueKey] - 1;
            }
        }
  );
  }(hiddenValueKey));

答案 1 :(得分:0)

您的代码存在一些问题。其中之一是keyhiddenValueKey设置在hookGetSet函数的范围内。因此,无论何时使用它们,都要使用循环中的最后一个值(3和__c)。您可以通过两种方式解决此问题:

  • 使用let代替var在循环范围内定义keyhiddenValueKey,但这仅适用于ES6
  • 使用闭包来限定循环内部

另一个问题是,在属性中你使用this.hiddenValueKey,它与this['hiddenValueKey']相同,而不是我认为你想要的this[hiddenValueKey]

这是有效的代码(EcmaScript6):

hookSetGet : function (someObject) {
    for (let key in someObject) {
        cc.log("key: " + key);

        // store the origin value before Object.defineProperty
        var pureValue = someObject[key];

        // add a property to store the encrypted value
        let hiddenValueKey = "__" + key;
        someObject[hiddenValueKey] = undefined;

        Object.defineProperty(
            someObject,
            key, {
            set : function (val) {
                // simulate encrypt
                this[hiddenValueKey] = val + 1000;
                cc.log("hooked set: " + val + " - " + this[hiddenValueKey]);
            },
            get : function () {
                // simulate decrypt
                var result = this[hiddenValueKey] - 1000;
                cc.log("hooked get: " + this[hiddenValueKey] + " - " + result);
                return result;
            }
        });

        // trigger set to encrypt
        someObject[key] = pureValue;
    }
}

以下是经典ES5 Javascript的相同代码:

hookSetGet : function (someObject) {
    for (var k in someObject) {
        (function () {
            var key = k;
            cc.log("key: " + key);

            // store the origin value before Object.defineProperty
            var pureValue = someObject[key];

            // add a property to store the encrypted value
            var hiddenValueKey = "__" + key;
            someObject[hiddenValueKey] = undefined;

            Object.defineProperty(
                someObject,
                key, {
                set : function (val) {
                    // simulate encrypt
                    this[hiddenValueKey] = val + 1000;
                    cc.log("hooked set: " + val + " - " + this[hiddenValueKey]);
                },
                get : function () {
                    // simulate decrypt
                    var result = this[hiddenValueKey] - 1000;
                    cc.log("hooked get: " + this[hiddenValueKey] + " - " + result);
                    return result;
                }
            });

            // trigger set to encrypt
            someObject[key] = pureValue;
        })();
    }
}