Javascript, раскрывающий шаблон модуля, общедоступные свойства

Я пытаюсь разобраться в раскрывающемся шаблоне модуля в javascript. Меня озадачивают две вещи в следующем фрагменте кода.

        var Child = function () {
            var totalPoints = 100;
            addPoints = function (points) {
                totalPoints += points;
                return totalPoints;
            };
            getPoints = function () {
                return totalPoints;
            };
            return {
                points: totalPoints,
                addPoints: addPoints
            };
        };
        $(function () {
            var george = Child();
            console.log(george.points);
            console.log(george.addPoints(50));
            console.log(george.points);
        });
  1. Здесь в консоль записываются три значения: 100, 150, 100. Это говорит мне о том, что когда я вызываю «addPoints» со значением, переменная totalPoints не обновляется. Если я проверю значение totalPoints внутри функции addPoints, оно было правильно увеличено. Что происходит?

  2. Если я использую консоль для проверки window.addPoints или window.getPoints, я вижу, что, поскольку я не использовал «var» перед объявлениями функций, они были добавлены в глобальную область. Разве это не так? Большинство примеров, которые я рассматриваю, делают это.

Любые указатели с благодарностью получены.


person centralscru    schedule 17.01.2012    source источник


Ответы (2)


Вы передаете число здесь:

return {
    points: totalPoints,
    addPoints: addPoints
};

Этот фрагмент кода ничем не отличается от:

return {
    points: 100,
    addPoints: addPoints
};

Вы передаете значение; не ссылка на totalPoints (последнее невозможно в JavaScript). Поэтому, когда totalPoints изменяется, значение в объекте не меняется.


Использование функции

Самый простой способ обойти это — использовать функцию для получения самого актуального результата (например, getPoints, который у вас уже есть). Этот JSFiddle дает полный пример:

return {
    points: function(x) { return totalPoints; }, // always up-to-date
    addPoints: addPoints
};

Недостатком является то, что вызывающая сторона теперь должна запрашивать points как вызов функции:

console.log(george.points());

Использование геттеров и сеттеров

Другим решением является использование геттеров, которые могут получить обновленное значение всего лишь с помощью george.totalPoints, хотя геттеры (пока) широко не поддерживаются. Вы можете реализовать геттер следующим образом:

var obj = {};

obj.points = addPoints;

// add a "special" property to the object instead of normal notation
Object.defineProperty(obj, "totalPoints", {
    get: function() { // function that's executed when you use `.totalPoints`
        return totalPoints;
    }
});

return obj;

Во-вторых, удаление var делает функции глобальными, что правильно, но нежелательно. Вы можете сделать одно утверждение var, используя запятые, если это то, что вы имеете в виду:

var totalPoints = 100, // expands to three `var` statements, so no
    addPoints = ...,   // global variables
    getPoints = ...;
person pimvdb    schedule 17.01.2012
comment
Большое спасибо, это действительно полезно! - person centralscru; 17.01.2012
comment
Спасибо. Это было очень полезно, поэтому я написал пост о том, что нужны сеттеры и геттеры geekswithblogs.net/Aligned/archive/2013/01/15/. Надеюсь, это поможет кому-то. - person AlignedDev; 24.01.2013
comment
+1 обнаружение ошибки, связанной с этим, сводило меня с ума последние 45 минут! - person weiglt; 15.09.2013

Это немного другое решение, которое не требует геттера/сеттера и сохраняет totalPoints как свойство.

    var Child = function () {

        var _api;

        return _api = {

            addPoints: addPoints,
            points: 100
        };

        /* ----------- */

        function addPoints(points) {
            _api.points += points;
            return _api.points;
        };

    };
person james    schedule 20.03.2015
comment
Это обеспечивает поле типа геттера, но не сеттер - это все еще функция. - person Chris Walsh; 01.06.2015