kmdjs和迴圈依賴

【當耐特】發表於2016-06-13

迴圈依賴

迴圈依賴是非常必要的,有的程式寫著寫著就迴圈依賴了,可以提取出一個物件來共同依賴解決迴圈依賴,但是有時會破壞程式的邏輯自封閉和高內聚。所以沒解決好迴圈依賴的模組化庫、框架、編譯器都不是一個好庫、框架、編譯器。

kmdjs的本質就是{},從{}擴充套件出的tree。從很早的版本就開始,是支援迴圈依賴的。比如下面的程式碼:

define('namespace1.A',['namespace2'], {
    ctor: function () {
        this.b = new B();
    }
})

define('namespace2.B',['namespace1'] , {
    ctor: function () {

    },
    xx: function () {
        var a = new A();
    }
})

會被kmdjs編譯成:

var namespace1 = {};
var namespace2 = {};

namespace1.A = Class.extend({
    ctor: function () {
        this.b = new namespace2.B();
    }
})

namespace2.B = Class.extend({
    ctor: function () {

    },
    xx: function () {
        var a = new namespace1.A();
    }
})

要支援迴圈依賴其實有個要求,就是lazy using不是lazy using的迴圈依賴是無解的迴圈依賴
怎麼理解上面這句話呢?看上面的程式碼,Class.extend執行完之後,各自依賴的東西是不會被呼叫的。
A程式碼裡的new namespace2.B()要在new namespace1.A的時候才會被呼叫。
B程式碼裡的new namespace1.A()要var a = new namespace1.A;a.xx()之後被呼叫後才會被執行。
所以在初始化階段,這樣的迴圈依賴是被允許的,因為都是lazy using。只要滿足lazy using,執行順序就不重要了,如果不是lazy using(如靜態屬性方法的設定),執行順序就必須把依賴的項先執行。
如上面所說,不是所有的迴圈依賴都能夠解決的,比如看C#裡面的無解的迴圈依賴的例子:

namespace Project1
{
    public class A
    {
        public static B b = new B();
    }
}

namespace Project1
{
    public class B
    {
        public static A a = new A();
    }
}

上面的程式碼編譯時候就會報錯。怎麼改成有解,那麼就要lazy using:

namespace Project1
{
    public class A
    {
        public static B b = new B();
    }
}

namespace Project1
{
    public class B
    {
        public int testMethod()
        {
            A a = new A();
            return 1;
        }
    }
}

這樣的依賴編譯器是可以解決的。

kmdjs 0.1.4

kmd的意思是 kernel module definition。該版本和以前的主要變化如下:

  1. kmdjs檔案大小從以前的一萬多行程式碼變成了一百多行程式碼
  2. 從以前的namespace+class組織專案變成namespace+module組織專案

kmdjs API

kmdjs.config
用於配置 namespace + module和檔案路徑的mapping

kmdjs.config({
    'util.bom':'js/util/bom.js',
    'app.Ball':'js/ball.js',
    'util.dom':'js/util/dom.js',
    'util.dom.test':'js/util/test.js',
    'main': 'js/main.js'
});

kmdjs.main
程式的入口程式碼。
kmdjs.define
定義模組

kmdjs.define('main',['util.bom','app.Ball','util.dom.test'], function(bom,Ball,test) {

    var ball = new Ball(0, 0, 28, 1, -2, 'kmdjs');
    alert(test.m(3, 3));
    var vp = bom.getViewport();

    setInterval(function () {
        ball.tick();
        (ball.x + ball.r * 2 > vp[2] || ball.x < 0) && (ball.vx *= -1);
        (ball.y + ball.r * 2 > vp[3] || ball.y < 0) && (ball.vy *= -1);
    }, 15);

});

如果只傳兩個引數,代表不依賴任何模組。這裡和AMD一樣,在factory的回撥裡把依賴注入到裡面。
但是也直接在程式碼裡把namespace寫進去訪問,如下所示:

kmdjs.define('main',['util.bom','app.Ball'], function() {

    var ball = new app.Ball(0, 0, 28, 1, -2, 'kmdjs');
    var vp = util.bom.getViewport();

    setInterval(function () {
        ball.tick();
        (ball.x + ball.r * 2 > vp[2] || ball.x < 0) && (ball.vx *= -1);
        (ball.y + ball.r * 2 > vp[3] || ball.y < 0) && (ball.vy *= -1);
    }, 15);

});

而有的時候必須使用上面這種方式用來解決迴圈依賴導致執行順序問題帶來的注入undefined:如:

kmdjs.define("util.dom",['util.bom'] ,function(bom){
    var Dom={};

    Dom.add = function(a,b){
        //迴圈依賴導致的bom undefined,所以這裡寫上namespace
        return util.bom.sub(a,b);
    }

    return Dom;
});

kmdjs.define("util.bom",["util.dom"], function(dom){
    var Bom={};

    Bom.getViewport=function() {
        alert(dom.add(1,4));
      
    };

    Bom.sub = function(a,b){
        return a-b;
    };
    return Bom;
});

bundler

可以通過main傳入回撥函式,在回撥函式中拿到編輯打包好的字串。

kmdjs.main(function(bundler){
    alert(bundler)
});

如上面的例子打包出來的程式碼:

var util={};
var app={};
util.dom={};
var main={};

util.dom = (function (bom){
    var Dom={};

    Dom.add = function(a,b){
        return util.bom.sub(a,b);
    }

    return Dom;
})(util.bom);

app.Ball = (function (){
    var Ball = function (x, y, r, vx, vy, text) {
        this.x = x;
        this.y = y;
        this.r = r;
        this.d = 2 * r;
        this.vx = vx;
        this.vy = vy;
        this.element = document.createElement("div");
        this.element.innerHTML = text;

        this.element.style.cssText = "text-align:center;position:absolute; -moz-border-radius:" + this.d + "px; border-radius: " + this.d + "px; width: " + this.d + "px; height: " + this.d + "px;background-color:green;line-height:" + this.d + "px;color:white;";
        document.body.appendChild(this.element);

    };

    Ball.prototype.tick= function () {
        this.x += this.vx;
        this.y += this.vy;
        this.element.style.left = this.x + "px";
        this.element.style.top = this.y + "px";
    };

    return Ball;
})();

util.dom.test = (function (){
    var Element={};

    Element.m = function(a,b){
        return a*b;
    }

    return Element;
})();

util.bom = (function (dom){
    var Bom={};

    Bom.getViewport=function() {
        alert(dom.add(1,4));
        var d = document.documentElement, b = document.body, w = window, div = document.createElement("div");
        div.innerHTML = "  <div></div>";
        var lt = !(div.firstChild.nodeType === 3) ?
        { left: b.scrollLeft || d.scrollLeft, top: b.scrollTop || d.scrollTop } :
        { left: w.pageXOffset, top: w.pageYOffset };
        var wh = w.innerWidth ?
        { width: w.innerWidth, height: w.innerHeight } :
            (d && d.clientWidth && d.clientWidth != 0 ?
            { width: d.clientWidth, height: d.clientHeight } :
            { width: b.clientWidth, height: b.clientHeight });

        return [lt.left, lt.top, wh.width, wh.height]
    };

    Bom.sub = function(a,b){
        return a-b;
    };
    return Bom;
})(util.dom);

main = (function (bom,Ball,test) {

    var ball = new Ball(0, 0, 28, 1, -2, 'kmdjs');
    alert(test.m(3, 3));
    var vp = bom.getViewport();

    setInterval(function () {
        ball.tick();
        (ball.x + ball.r * 2 > vp[2] || ball.x < 0) && (ball.vx *= -1);
        (ball.y + ball.r * 2 > vp[3] || ball.y < 0) && (ball.vy *= -1);
    }, 15);

})(util.bom,app.Ball,util.dom.test);

Github

https://github.com/kmdjs/kmdjs

相關文章