4

我目前正在尝试在小项目中实现一些常见的 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)? 有没有更好的办法?

非常感谢您的帮助,

罗宾

4

2 回答 2

4

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

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

调用obj.method()会自动将此调用中的this引用设置为. 也就是说,调用集合到这个方法调用内部。有关此行为的更多详细信息,请点击此处methodobjMYAPP.game.startGame()thisMYAPP.gamestartGame

您还需要将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();
        }
    }
})();

小提琴

lives在这种情况下,当您想要更改变量时,您将需要一些方法来设置变量。另一种方法是通过将变量设为返回对象的属性并通过方法内部lives访问它来使变量公开。this.lives

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

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

thisclick 处理程序中的引用不会指向,MYAPP.game因为传递的函数引用将直接从 jQuery 核心调用,而不是作为对象的成员函数调用 - 在这种情况下,this将指向#button元素,因为 jQuery 事件处理程序设置this引用触发处理程序的元素,如您在此处看到的。

要解决这个问题,您可以使用Function.bind()

$('#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
});

this当在方法中使用关键字时,这一点很重要update

于 2013-04-28T07:49:50.147 回答
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) 

});

在 jsfiddle 上演示

于 2013-04-28T07:55:14.990 回答