关于如何使用模块模式的困惑

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于如何使用模块模式的困惑相关的知识,希望对你有一定的参考价值。

我对如何在javascript中使用模块模式(以及一般的设计模式)感到困惑。

我已经在我的应用程序中使用模块模式编写了一些正常运行的代码,它实现了我想要的功能,但它对我来说似乎并不是非常模块化的,而且我一直觉得我做错了。我没有找到任何设计模式的具体和完整的应用程序示例。

以下是我如何使用它:

假设我在我的应用程序中有表单,我将用于不同的模块(发布一个帖子,回复一个帖子,评论客人的书),用一些JavaScript我会给用户一些功能,比如弹出一个笑脸泡泡并处理在我的表单中插入它们,将数据发送到我的服务器代码以返回html代码,以便在不重新加载页面的情况下添加消息,我会做类似的事情:

    let Form = function (selector_form, selector_textarea, selector_emoticonsButton, selector_postButton) 

      let form, textarea, emoticonsButton, postButton;
      let emoticonsBubble = new EmoticonsBubble()

      return 
        selectors: function () 
          return 
            form: function ()  return selector_form ,
            sendButton: function ()  return selector_sendButton 
          
        

        setElements: function (obj) 
          form = $(obj).get(0);
          textarea = $(form).find(selector_textarea).get(0);
          emoticonsButton = $(form).find(emoticonsButton).get(0);
          postButton = $(form).find(selector_postButton).get(0);

          emoticonsBubble.setElements(form, emoticonsButton);
        ,

        get: function () 
          return 
            form: function ()  return form ,
            //...
            emoticonsBubble: function ()  return emoticonsBubble 
          
        ,

        post: function (moduleId, callback) 
          $.ajax(
          //parameters
          ).done(function (data) 
            callback(data);
          );
        
      
    

    let EmoticonsBubble = function () 

      let thisContainerToAppendTo, thisTextarea;

      return 
        setElements: function (container, textarea) 
          thisContainerToAppendTo = container;
          thisTextarea = textarea;
        ,

        pop: function () 
          this.ajax().pop(function (data) 
            $(thisContainerToAppendTo).append(data);
          );
        

        insert: function (emoticon) 
          $(thisTextarea).append(emoticon);
        ,

        ajax: function () 
          return 
            pop: function (callback) 
              $.ajax(
              //parameters
              ).done(function (data) 
                callback(data);
              );
            
          
        
      
    

    // Events part

    let form = new Form('#threadForm', '.textarea', 'button[name="emoticons"]', 'button[name="send"]');
    let emoticonsBubble = form.get().emoticonsBubble();

    $(form.selectors().form()).on('click', function (e) 
      form.setElements(this);
    );

    $(form.selectors().sendButton()).on('click', function (e) 
      let moduleId = // retrieve module id, if it belongs to guests book, thread creation module or reply module
      form.post(moduleId, function (data) 
        // append data to something
      );
    );

    // etc for emoticons handling

事实上,我必须为我的应用程序中的每个不同形式重写事件部分,同时保持一切相同但变量名称,让我很烦恼。

你能告诉我你将如何处理这些功能以及我的编码方式可能出现的问题?

答案

模块模式是关于保持代码单元不与其他范围(通常是全局范围)冲突。

我们知道,在JavaScript中,变量定义为:

  • letconst的范围是他们的父级
  • var的范围包含其包含的函数(如果不在函数中,则为Global)

那么,如果你要采取你的Form功能:

let Form = function (x,y,z) 

  let form, textarea, emoticonsButton, postButton;
  let emoticonsBubble = new EmoticonsBubble()

  return 
        . . . 
    

    setElements: function (obj) 
        . . . 
    ,

    get: function () 
        . . . 
    ,

    post: function (moduleId, callback) 
        . . . 
    
  

变量Form是Global,因为没有包含块。这是一个问题,因为如果已经存在另一个名为Form的全局(由于“表单”这个词的通用性质,很可能会出现这种情况)。因此,此代码不会切断您的代码被暴露。要在其上使用模块模式,我们用IIFE(立即调用的函数表达式)包装它,在IIFE中,我们在全局范围内创建一个我们确定不存在的自定义命名空间(从而避免名称冲突):

(function()
  // This is going to be exposed as publicly available via the module namespace
  function Form(x,y,z) 
    . . .
  

  // This will remain private within the module
  function helper()

  

  // **********************************************************************    
  let temp = ;    // Create a temporary object to bind only the public API
  temp.Form = Form; // Bind the public members to the object

  // Expose the module to the Global scope by creating a custom namespace 
  // and mapping the temp object to it
  window.myCustomAPI = temp;
)();

// Now, outside of the module (in some higher scope), your public portions
// of the Module are accessible:
let myForm = new myCustomAPI.Form(arg, arg, arg);
另一答案

代码中的重复主要来自元素及其助手的选择,可以很容易地将其抽象为函数:

  function Elements(selectors, children, options) 
    let elements =  ...children ;

    return 
      selectors, 
      elements,
      setElements(obj)                 
        for(const [name, selector] of Object.entries(selectors)) 
           elements[name] = $(obj).find(selector).get(0);
        for(const child of Object.values(child))
           child.parent && child.parent(this, obj);
       ,
       ...options
    
 

然后可以用作:

  function Form(form, textarea, emoticonsButton, postButton) 
     const emoticonsBubble = EmoticonsBubble();

     return Elements( form, textarea, emoticonButtons ,  emoticonsBubble , 
       post() 
         //...
       
    );
 

 function EmoticonsBubble() 
   return Elements( /*...*/ , , 
      parent(parent, obj) 
        this.setElements(parent);
      
   );
 

但是你基本上在这里重新发明了很多轮子,你有没有想过使用其中一个MVC(React,Vue,......)?

另一答案

在事件部分中您遇到的一些常见任务的样板是否让您疯狂?

因此,检查代码可以通过多种方式修复它们。

A.将代码封装在真实模块中我的意思是这个。

const Form = (function(/*receive here dependencies as arguments */)
  // your code module goes here 
)(/*inject dependencies here to module*/);

B.您可以创建一个event pattern模块,以驱动模块的内部和外部事件。

C.您知道模块需要什么样的监听器,因此将它们应用到您的模块中。

这种方式应该比现在更容易重复使用

以上是关于关于如何使用模块模式的困惑的主要内容,如果未能解决你的问题,请参考以下文章

关于 initWithNavigationBarClass 的困惑 - 如何使用(新的 instanceType 方法)

关于如何使用显式绑定方法的困惑

关于 vim 折叠的困惑 - 如何禁用?

如何在Android中计算行驶方向模式下两点之间的距离

关于 sprintf 函数如何在 C 中工作的困惑

对如何从模块中的其他目录/父目录导入感到困惑(python 3)