requirejs 循环依赖和函数对象

requirejs circular dependencies and function objects

是否可以区分结构构建所需的依赖项(如继承)和运行时依赖项(在方法调用中)。

一个小例子:

2 "classes": 相依为命的父子

Father.js

define(['Child'], function (Child) {

    function Father() {};    

    Father.prototype.childs = [];

    Father.prototype.addChild = function (c) {
        if (!(c instanceof Child)) {
            alert("nope");
        }
    }

    return Father;
});

Child.js

define(['Father'], function(Father){

   function Child(){
       this.father = [];
   }

   Child.prototype.setFather = function(f){
       if(!(f instanceof Father)){
           alert("false");
       }
   }

   return Child;
});

和一个app.js

requirejs(['Child', 'Father'], function(Child, Father){
    var c = new Child();
    var f = new Father();
    c.setFather(f);
    f.addChild(c);
});

当使用 export 时,您只能扩展一个对象(如果我是正确的)。那么是否可以构建这样的结构?

我实际尝试做的事情:有一个自动的"class"-loading(分离的文件),它将所有依赖项加载到一个更大的模型中,它有一些循环依赖项。一些依赖性立即需要(继承),而一些仅在对象启动后才需要。而且我找不到解决问题的好方法。

这是 (IMO) 一个不同的有效问题,从昨天开始我就困惑了。如果我是你,我会考虑什么,按优先顺序:

  1. 拥抱鸭子打字。一个有用的 Child class 可能有像 play()cry() 这样的方法,所以 "if it walks like a duck and quacks like a duck..." 变成 "if it plays like child and cries like a child, then it is a child":

    // Father.js
    define([], function() {
    
        function Father() {};    
    
        Father.prototype.childs = [];
    
        Father.prototype.addChild = function (c) {
            if( !c || typeof(c.play) !== 'function' || typeof(c.cry) !== 'function' ) {
                alert("nope");
            }
        }
    
        return Father;
    });
    

    这种方法的另一个好处(在一般情况下)是您实际上是针对 Javascript 中的等效接口进行编程。 IE。思维过程是"What am I going to need a Child for? -Playing and crying. -Then it is enough for me that the object I get has these 2 methods. I do not care about the exact implementation of it."

  2. 这是技术性的且丑陋,但(至少在 IMO 上)丑陋得可以接受:您可以引入第三个依赖项来打破循环,这在循环中很常见。即:

    // Father.js
    define([], function() {
    
        function Father() {};    
    
        Father.prototype.childs = [];
    
        return Father;
    });
    
    // Child.js
    define(['Father'], function(Father){
    
        function Child(){
            this.father = [];
        }
    
        Child.prototype.setFather = function(f){
            if(!(f instanceof Father)){
                alert("false");
            }
        }
    
        return Child;
    });
    
    // FatherAugmentor.js (any ideas for a better name?)
    define(['Father', 'Child'], function(Father, Child) {
        Father.prototype.addChild = function (c) {
            if (!(c instanceof Child)) {
                alert("nope");
            }
        }
    });
    

    一个问题: 您必须确保从某处需要 FatherAugmentor!或者,您可以使用名称(再次丑陋),以便上面的 FatherAugmentor 变成 Father 并且上面的 Father 重命名为,例如FatherBase:

    // FatherBase.js
    define([], function() {
        // exactly the same as `Father.js` from above
    });
    
    // Child.js
    define(['FatherBase'], function(Father){
        // exactly the same as `Child.js` from above
    });
    
    // Father.js
    define(['FatherBase', 'Child'], function(Father, Child) {
        // exactly the same as `FatherAugmentor.js` from above
    });
    

    有了这个,您可以确保任何请求 Father 将获得完整的 class,但您必须小心地在作为循环一部分的文件中使用 FatherBase(即 Child.jsFather.js)。

  3. 可以考虑涉及命名空间对象的解决方案(即 return 一个包含 family.Fatherfamily.Child 构造函数的 family 对象),但我觉得它太丑了。

我知道这不是很好并且适当地打破了一些优化和一些约定(比如处理具有相同名称的模块)。所以这是一个复制全局范围内所有模块的解决方案。

Child.js

define(['Father'], function(){

//   var Father = require("Father");
   function Child(){
       this.father = [];
   }

   Child.prototype.setFather = function(f){
       if(!(f instanceof Father)){
           alert("false");
       }
   }

   window.Child = Child;
});

Father.js

define(['Child'], function () {

    function Father() {};    

    Father.prototype.childs = [];

    Father.prototype.addChild = function (c) {
        if (!(c instanceof Child)) {
            alert("nope");
        }
    };

    window.Father = Father;
});

app.js

requirejs(['Child', 'Father'], function(){
    var c = new Child();
    var f = new Father();
    c.setFather(f);
    f.addChild(c);
    console.log("done");
});

为了同步加载(在一个更复杂的项目上),我使用了一个中介来跟踪所需的加载,并在加载所有内容时触发一个事件。

您可以使用 而不是 window 在其他平台上工作。