循环依赖
循环依赖是非常必要的,有的程序写着写着就循环依赖了,可以提取出一个对象来共同依赖解决循环依赖,但是有时会破坏程序的逻辑自封闭和高内聚。所以没解决好循环依赖的模块化库、框架、编译器都不是一个好库、框架、编译器。
kmdjs 的本质就是 {},从 {} 扩展出的 tree。从很早的版本就开始,是支持循环依赖的。比如下面的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
define('namespace1.A',['namespace2'], { ctor: function () { this.b = new B(); } }) define('namespace2.B',['namespace1'] , { ctor: function () { }, xx: function () { var a = new A(); } }) |
会被 kmdjs 编译成:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
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#里面的无解的循环依赖的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
namespace Project1 { public class A { public static B b = new B(); } } namespace Project1 { public class B { public static A a = new A(); } } |
上面的代码编译时候就会报错。怎么改成有解,那么就要 lazy using:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
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。该版本和以前的主要变化如下:
- kmdjs 文件大小从以前的一万多行代码变成了一百多行代码
- 从以前的 namespace+class 组织项目变成 namespace+module 组织项目
kmdjs API
kmdjs.config
用于配置 namespace + module 和文件路径的 mapping
1 2 3 4 5 6 7 |
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
定义模块
1 2 3 4 5 6 7 8 9 10 11 12 13 |
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 写进去访问,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 |
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:如:
1 2 3 4 5 6 7 8 9 10 |
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; }); |
和
1 2 3 4 5 6 7 8 9 10 11 12 13 |
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 传入回调函数,在回调函数中拿到编辑打包好的字符串。
1 2 3 |
kmdjs.main(function(bundler){ alert(bundler) }); |
如上面的例子打包出来的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
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); |
极乐网 2016 年 8 月 30 日
沙发!!!!!!!!!!个人建站,做技术问答的,目前最火的是前端领域,欢迎一起交流!——http://www.dreawer.com