Максимальный стек превышен при наблюдении значения определенного свойства

Я получаю Maximum call stack size exceeded ошибка всякий раз, когда я пытаюсь использовать Object.observeнаблюдать изменения в объекте, для которого я определил свойства черезObject.defineProperty,

Как правильно обойти эту ошибку, но при этом можно использовать оба этих метода?

Замечания: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>

2 ответа

Решение

Вы читаете и пишете в одну и ту же переменную снова и снова в Object.defineProperty...

Вы должны изменить имя this.testValue в первой строке TestClass. Я бы предложил переименовать его в 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
    });
}
Другие вопросы по тегам