观察定义值 属性 时超出最大堆栈

Maximum stack exceeded when observing value of defined property

每当我尝试使用 Object.observe to observe changes in an object that I defined properties for through Object.defineProperty.

时,我都会收到 Maximum call stack size exceeded 错误

在仍然能够使用这两种方法的同时避免抛出此错误的正确方法是什么?

注意:Object.observe 仅适用于 Chrome 和 Opera

var TestModule = (function () {
    "use strict";

    function TestClass() {
        this.testValue = 0;
        Object.defineProperty(this, "testValue", {
            get: function () {
                return this.testValue;
            },
            set: function (value) {
                this.testValue = value;
            },
            enumerable: true,
            configurable: false
        });
    }

    return {
        TestClass: TestClass
    };
}());
<!DOCTYPE html>

<head>
    <title>Stack Exceed Test</title>
    <script src="../js/TestModule.js"></script>
</head>

<body>
    <main>
        <div id="logger" role="log"></div>
    </main>
    <script>
        document.addEventListener("DOMContentLoaded", function () {
            var logger = document.getElementById("logger"),
                tc = new TestModule.TestClass();

            function log(message) {
                if (logger) {
                    logger.innerHTML = message;
                } else {
                    console.error(message);
                }
            }

            if (typeof Object.observe === "function") {
                Object.observe(tc, function (changes) {
                    console.log("Change");
                });

                try {
                    tc.testValue = 5;
                } catch (e) {
                    log(e);
                }
            } else {
                log("Object.observe is unsupported in your browser");
            }
        });
    </script>
</body>

您在 Object.defineProperty...

中一遍又一遍地读写同一个变量

您应该在 TestClass 的第一行更改 this.testValue 的名称。我建议将其重命名为 this._testValue,这是命名变量的约定,以表明它们是 "private".

注意,您也可以保留 this.testValue 并完全删除 Object.defineProperty... 部分,因为您所做的只是读取和写入默认值。

var TestModule = (function () {
    "use strict";

    function TestClass() {
        this._testValue = 0;
        Object.defineProperty(this, "testValue", {
            get: function () {
                return this._testValue;
            },
            set: function (value) {
                this._testValue = value;
            },
            enumerable: true,
            configurable: false
        });
    }

    return {
        TestClass: TestClass
    };
}());
<!DOCTYPE html>

<head>
    <title>Stack Exceed Test</title>
    <script src="../js/TestModule.js"></script>
</head>

<body>
    <main>
        <div id="logger" role="log"></div>
    </main>
    <script>
        document.addEventListener("DOMContentLoaded", function () {
            var logger = document.getElementById("logger"),
                tc = new TestModule.TestClass();

            function log(message) {
                if (logger) {
                    logger.innerHTML = message;
                } else {
                    console.error(message);
                }
            }

            if (typeof Object.observe === "function") {
                Object.observe(tc, function (changes) {
                    console.log("Change");
                });

                try {
                    tc.testValue = 5;
                } catch (e) {
                    log(e);
                }
            } else {
                log("Object.observe is unsupported in your browser");
            }
        });
    </script>
</body>

另一种解决此问题的方法,如果您不想通过间接方式对 "wrap" 您的值进行排序,是使用 Object.getNotifier(),它允许您手动发出通知并保留本地不是您的对象成员的变量。

如果您使用通知程序,您就可以避免拥有实际上不会被使用的对象属性。如果使用包装方法,则对象上将同时具有 _testValue testValue。使用通知程序,您将 只有 testValue.

考虑代码更改:

function TestClass() {
    var testValue, notifier;
    /* 
     * The new locally scoped varible which will
     * be captured by the getter/setter closure
     */
    testValue = 0;

    /*
     * Create a notifier for the object
     * which we can use to trigger 
     * Object.observe events manually
     */
    notifier = Object.getNotifier(this);

    Object.defineProperty(this, "testValue", {
        get: function () {
            return testValue;
        },
        set: function (value) {
            /*
             * Use the notifier to trigger 
             * the observe()
             */
            notifier.notify({
                type: "update",
                name: "testValue",
                oldValue: testValue
            });
            testValue = value;
        },
        enumerable: true,
        configurable: false
    });
}