【问题标题】:Best way to combine the module pattern with closures将模块模式与闭包相结合的最佳方式
【发布时间】:2013-04-22 01:17:27
【问题描述】:

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

非常感谢您的帮助,

罗宾

【问题讨论】:

    标签: javascript design-patterns closures


    【解决方案1】:

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

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

    调用obj.method() 会自动将此method 调用中的this 引用设置为obj。也就是说,在此 startGame 方法调用中调用 MYAPP.game.startGame() 会将 this 设置为 MYAPP.game。有关此行为的更多详细信息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 访问它来公开lives 变量。

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

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

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

    为了补救你可以使用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 方法中使用时,这一点很重要。

    【讨论】:

      【解决方案2】:

      您的代码中存在一些问题。首先,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)

      【讨论】:

      • 谢谢 - 说得通
      猜你喜欢
      • 2012-01-30
      • 1970-01-01
      • 1970-01-01
      • 2020-04-29
      • 2021-04-23
      • 1970-01-01
      • 1970-01-01
      • 2016-03-01
      • 1970-01-01
      相关资源
      最近更新 更多