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

Maximum stack exceeded when observing value of defined property

本文关键字:大堆栈 定义 属性 观察      更新时间:2023-09-26

每当我尝试使用Object.observe来观察我通过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,这是一种命名变量以指示它们是"私有"的约定。

注意,您也可以保留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>

另一种解决这个问题的方法,如果你不想用间接的方式"包装"你的值,是使用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
    });
}