【问题标题】:Circular dependencies in JavaScript OOPJavaScript OOP 中的循环依赖
【发布时间】:2014-05-05 17:57:10
【问题描述】:
// Main class
function App() {
    this.task = new Task(this); // pass the instance of this class to Task so 
                                // it has access to doSomething
}

App.prototype.doSomething = function () {
    alert("I do something that Task() needs to be able to do!");
};

function Task(app) {
    // This class needs access to App()'s doSomething method
    this.appInstance = app;
    this.appInstance.doSomething(); // Great, now Task can call the method
}

var app = new App();

上述代码的目的是让 Task 访问 App 的方法之一,称为doSomething。代码是我目前的处理方式,我发布这个看看它是否是最好的方式......

要授予任务访问权限,我只需传递 App 的整个实例,这是有效的还是有更好的方法呢?上面的代码是做这样的事情的一般做法吗?

【问题讨论】:

  • 你做对了。
  • 为什么App 需要知道Task
  • 我觉得这个例子应该大一点,才能全面分析方法。目前它足够小,可能不明显的不良做法。
  • @IonuțG.Stan 因为我已经说过了 ;) - 这是一个例子,在我的生产代码中有很多情况下子类需要访问主类中的信息
  • @TravisJ 是的,这很公平。在我的生产代码中,每个类中大约还有 10-20 个方法,但是关于继承实例的一般问题是相同的

标签: javascript oop circular-dependency


【解决方案1】:

是的,你所拥有的一切都很好。这是一个循环依赖,但由于 JavaScript 的动态特性,实际上并没有任何问题。

Task 引用App 的另一种方法是单例模式或类似的东西,但这可能更难测试。

【讨论】:

    【解决方案2】:

    jsFiddle Demo

    一般情况下,bind 会在这种情况下使用,前提是 Task“类”也没有设置此处未显示的其他设施。

    Bind 允许为函数提供上下文。这可以在应用程序的构造函数中完成。此时只需要一个函数task 来调用"someMethod"

    function task(){
        return this["someMethod"]();
    }
    
    function App(){
        task.bind(this)();
    }
    
    App.prototype.someMethod = function(){
        alert("Task needed access to this");
    };
    
    var a = new App();
    

    但是,如果task 必须是一个“类”,并且有其他职责,那么原型函数可以共享。

    function Task(){}
    function App(){}
    
    App.prototype.someMethod = Task.prototype.someMethod = function(){
        alert("Task needed access to this");
    };
    
    var a = new App();
    a.task();//->"Task needed access to this"
    var t = new Task();
    t.someMethod();//->"Task needed access to this"
    

    【讨论】:

      【解决方案3】:

      您的应用实例和任务实例是紧密绑定的。应用实例有任务,这很好。

      松散耦合对象的设计更灵活,更容易扩展,但初始创建更复杂。一种这样的模式是使用调解器/发布订阅者并让应用程序引发事件/发布消息,任何其他对象函数都可以侦听此事件并对事件采取行动。

      例如:您的应用创建了一个 Ajax 实例,当该实例完成时,它会引发一些事件(例如 fetchedData)。侦听器可以是 DomDependent.updateView 函数,但稍后您可能希望在获取数据后添加/删除/更改要执行的任务的顺序。这都可以在 app.init 函数中配置,也可以在启动某些过程(如登录、搜索等)的控制器中的每个过程中进行配置。

      您可以创建一个通用函数并让控制器添加侦听器或在 fetchData 完成时传递下一个事件以运行正确的下一个函数。

      这是一些伪代码:

      var app = {
        init:function(){
          mediator.add("updateLogin",domDependent.updateView);
          mediator.add("updateLogin",app.loadUserPrefs);
          mediator.add("failLogin",domDependent.updateView);
        },
        login: function(){
          mediator.trigger("loadingSometing",{type:"login"});
          ajax.fetch({
            onComplete:"updateLogin",//what listens to updateLogin you decided in init
            onFail:"failLogin",
            loginDetails:domDependent.getLogin(),
            url:settings.loginUrl,
            type:"post"
          });
        }
      }
      
      var ajax = {
        fetch:function(data){
          data = data || {};
          //simple check for onComplete, it's mandatory
          var complete = data.onComplete || app.raiseError("ajax.fetch needs onComplete");
          //other code to validate data and making ajax request
          onSuccess:function(resp){
            //mutate data object as the mediator will pass it to
            //  whatever other function is called next
            //  you don't hard code domDependent.updateView and
            //  app.loadUserPrefs because fetch can be used generally and
            //  success may have to do completely different things after its done
            //  and you want to define procedures in init, not all over your code
            data.response=resp;
            //trigger event to do whatever needs to be done next
            mediator.trigger(complete,data);
          }
        }
      }
      

      如您所见,它变得复杂,可能看起来不像您习惯的代码,但它是高度可配置的。

      我可能误解了中介模式对松散夫妇的优势,如果是,请发表评论。我用它来:

      1. 使方法更通用,而不是仅仅复制大量逻辑 因为做完之后要做什么是不一样的。在获取ajax 对象只是获取,这对于登录或获取是相同的 用户偏好,唯一不同的是调用什么函数 完成时出现 next/on 错误。

      2. 像登录这样的过程涉及多个功能 对象,如果这个函数链硬编码下一步做什么 特定功能已完成您的登录程序已全部定义 在你的代码上。在 init/config 中定义它时,您可以轻松更改 在链中排序或添加/删除函数。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-10-21
        • 1970-01-01
        • 2021-03-25
        • 1970-01-01
        • 2016-08-24
        • 1970-01-01
        • 2010-09-12
        相关资源
        最近更新 更多