1

Is it possible to differentiate between dependencies which are needed on structure build (like inheritance) and runtime dependencies (within a method call).

A little example:

2 "classes": Father and Child which depend on each other

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

and an app.js

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

When using export you can only extend an object (if i'm correct). So is it possible to build a structure like this ?

What i actually try to do: have an automatic "class"-loading (seperated files), which loads all dependencies within a bigger model, which has some circular dependencies. Some dependencies needed right away (inheritance) and some are only needed after object initiation. And I cant find a nice solution for my problem.

2 Answers 2

2

This is (IMO) a vary valid question, puzzled me since yesterday. What I would consider if I were you, in preference order:

  1. Embrace ducktyping. A useful Child class may have methods like play() and cry(), so the "if it walks like a duck and quacks like a duck..." becomes "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;
    });
    

    Another benefit of this approach (in the general case) is that you are actually programming against the equivalent of interfaces in Javascript. I.e. the thought process is "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. This is technical and ugly, but (at least IMO) acceptably ugly: You can introduce a 3rd dependency to break the cycle as is common with cycles. I.e.:

    // 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");
            }
        }
    });
    

    A catch: you must make sure that the FatherAugmentor is required from somewhere! Alternatively, you can play with names (ugly again), so that the FatherAugmentor from above becomes the Father and the Father from above is renamed to, e.g. 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
    });
    

    With this you make sure that anybody that requests the Father will get the full class, but you have to be careful to use the FatherBase in the files that are parts of the cycle (i.e. Child.js and Father.js).

  3. One could consider solutions involving a namespace object (i.e. return a family object that contains the constructors as family.Father and family.Child), but I think it gets too ugly.

Sign up to request clarification or add additional context in comments.

1 Comment

those alternatives are nice and properly should be considered on a big refactor. I only need it in a dev enviroment, the code will be generated into a diffrent structure for the productive system. I made a little nastier solution
-1

I know this is not a very nice and properly breaks some optimization and some convention (like handling modules with the same name). So here is a solution wich copies all the modules in the global scope.

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");
});

To sync the loading (on a more complex project) i use a mediator which keeps track of needed loading and which fires an event when everything is loaded.

instead of window you can use https://stackoverflow.com/a/5573049/228763 to work with on other platforms.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.