混淆如何使用模块模式

时间:2019-04-19 12:43:55

标签: javascript events design-patterns

我对如何使用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

我必须为应用程序中具有的每种不同形式重写事件部分,而除了变量名之外,其他所有内容都保持不变,这一事实使我非常烦恼。

你们能告诉我如何处理这些功能以及我的编码方式可能有什么问题吗?

3 个答案:

答案 0 :(得分:1)

代码中的重复基本上来自元素及其辅助对象的选择,可以很容易地抽象为一个函数:

  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等)?

答案 1 :(得分:1)

好吧,如果您遇到了一些常见的任务,就会使自己发疯了吗?

因此,您可以通过多种方式来检查代码。

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

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

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

C。您知道该模块需要什么侦听器,因此将它们应用于您的模块。

这种方式应该比现在更可重用

答案 2 :(得分:1)

模块模式用于防止代码单元与其他作用域(通常是全局作用域)冲突。

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

  • letconst的范围仅限于其父块
  • var的范围仅限于其包含函数(如果不在 功能)

因此,如果要使用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为全局变量,因为没有包含块。这是一个问题,因为如果已经有另一个名为Form的全局变量(那很可能是由于“表单”一词的一般性质)怎么办?因此,此代码不会阻止您的代码被公开。要在其上使用模块模式,我们将其与IIFE(立即调用函数表达式)包装在一起,并在该IIFE中,我们将在Global范围内创建一个自定义名称空间,以确保该名称空间不存在(从而避免名称冲突):

(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);