将模块模式与闭包相结合的最佳方法

时间:2013-04-28 07:19:57

标签: javascript design-patterns closures

我目前正在尝试实现一些常见的JS概念 在小项目中,更好地了解如何使用它们。

我一直在努力开发一款简单的游戏 理解并使用模块模式和闭包。 我正在使用Stoyan Stefanov的'模式'模块模式 书。

我很难理解如何最好地混合模块和 闭包。

我想知道我是否正在组织以下代码 明智的方式?如果是这样,我的问题是:什么是最好的方式 修改代码,以便在我$(function(){})中 访问update()功能?

MYAPP.utilities = (function() {

    return {
        fn1: function(lives) {
            //do stuff
        }
    }
})();

MYAPP.game = (function() {

    //dependencies
    utils = MYAPP.utilities

    return {
        startGame: function() {

            //initialisation code
            //game state, stored in closure
            var lives = 3;
            var victoryPoints = 0;

            function update(){
                utils.fn1(lives);
                //do other stuff
            }

        }
    }
})();

$(function(){
    MYAPP.game.startGame();

    //Want to do this, but it won't work 
    //because I don't have access to update

    $('#button').on('click',MYAPP.game.update) 

});

我想出了几个可行的方案,但是 我想知道他们是不是很好的做法,也是最好的做法 选项是。

选项:

(1)绑定$('#button').on('click', ...)作为一部分 startGame初始化代码。

(2)将update()函数分配给变量,和 从startGame函数返回此变量,所以在 我们可以$(function(){}) updatefn = MYAPP.game.startGame();然后 $('#button').on('click',MYAPP.game.update)

(3)?还有更好的方法吗?

非常感谢您的帮助,

罗宾

2 个答案:

答案 0 :(得分:4)

首先,要以这种方式访问​​update函数,它必须在返回的对象中公开。

return {
    update: function() {
        [...]
    },
    startGame: function() {
        [...]
        this.update();
    }
}

呼叫obj.method()会自动将此this来电中的method引用设置为obj。也就是说,在此MYAPP.game.startGame()方法调用中调用thisMYAPP.game设置为startGame。有关此行为的更多详细信息here

您还希望将lives变量移动到可由startGameupdate方法访问的公共范围,这正是闭包的用途:

MYAPP.game = (function() {

    [...]
    var lives; //private/privileged var inside the closure, only accessible by
               //the returned object's function properties

    return {
        update: function() {
            utils.fn1(lives);
        },
        startGame: function() {
            [...]
            lives = 3; //sets the closure scope's lives variable
            [...]
            this.update();
        }
    }
})();

Fiddle

在这种情况下,当您想要更改lives变量时,需要一些方法来设置它。另一种方法是通过使lives变量成为返回对象的属性并通过方法内的this.lives访问它来公开$('#button').on('click', MYAPP.game.update); 变量。

注意:如果您只是传递对作为返回对象的属性存储的函数对象的引用,如:

this

点击处理程序中的MYAPP.game引用将指向this,因为已传递的函数引用将直接从jQuery核心调用而不是作为object的成员函数调用 - 在这种情况下,#button将指向this元素,因为jQuery事件处理程序将$('#button').on('click', MYAPP.game.update.bind(MYAPP.game)); 引用设置为触发处理程序的元素,如您所见$('#button').on('click', function() { MYAPP.game.update(); //called as method of an obj, sets `this` to MYAPP.game }); 3}}

要解决此问题,您可以使用here

this

或旧的函数包装技巧:

update

在{{1}}方法中使用{{1}}关键字时,这很重要。

答案 1 :(得分:1)

您的代码中存在一些问题。首先,update()函数在您即时创建的对象外部不可见。要使其成为game对象的一部分,它必须与startGame处于同一级别。

此外,如果您声明var lives = 3,它将是一个局部变量,并且在startGame()函数以及victoryPoints之外将不可见。这两个变量必须以某种方式可见(通过闭包或作为对象字段)。

最后,将MYAPP.game.update作为事件侦听器附加将仅附加该函数,从而阻止您使用所有其他对象方法/函数。根据您的目的,您可能更愿意传递function() { MYAPP.game.update() }之类的闭包。

您的代码应该类似于:

MYAPP.utilities = (function() {

    return {
        fn1: function(lives) {
            console.log(lives);
        }
    }
})();

MYAPP.game = (function() {

    //dependencies
    utils = MYAPP.utilities

    var lives;
    var victoryPoints;

    return {
        startGame: function() {

            //initialisation code
            //game state, stored in closure
            lives = 3;
            victoryPoints = 0;
        },

        update: function() {
            utils.fn1(lives);
            //do other stuff
        }
    }
})();

$(function(){
    MYAPP.game.startGame();

    //Want to do this, but it won't work 
    //because I don't have access to update

    $('#button').on('click', MYAPP.game.update) 

});

(的 DEMO on jsfiddle