AMD and CMD are dead之js模塊化黑魔法

原因

在2013-03-06 13:58的時候,曾甩下一片文章叫:《爲何不使用requirejs和seajs》,並放下豪言說發佈一款完美的模塊化庫,再後來就把那篇文章刪了,再而後就沒有而後。該用seajs還用seajs,甚至我碼的SCJ都是用requirejs組織起來的。css

時光飛逝,歲月流轉。彈指間,來到了2014年6月15日,也就是昨日,忽然碼興大發,一發不可收拾,也許跟最近小說和詩寫得比較猛,把碼意給壓抑了,便有了此次噴發。html

js問題

做爲一名前MS必應團隊資深當耐特(.NET)石專家,拿js與C#開發應用開發作個對比,js主要暴露的問題有:web

1.沒有class關鍵字來定義類json

2.沒有namespace關鍵字來定義命名空間瀏覽器

3.沒有using/require/import/include關鍵字來處理依賴app

4.繼承、partial class、static、private、protected、publish等都要經過小技巧或者特定約定規範且手段太多 模塊化

AMD和CMD的問題

爲何要define(function(){return xx})?函數

爲何要本是同根生,還要deps?requirejs

爲何要module.export?測試

爲何要define(function(require, exports, module) {})?

爲何全部模塊都須要require deps才能使用?

別看多隻多寫了幾個單詞,但這絕對是掙扎糾結以後妥協的結果。

你要推翻它?那請制定一個更好的規範,OK?沒有就別瞎嚷嚷,OK?

規範

js裏一切define的東西皆class建立出來的

js中一切class都在namespace下

js中define("namespace.class",[namespaces],factory)用於把namespace和class名定義好,並可引用依賴的namespace,相似C#using

js中require用於引用依賴,相似於C#using

js中同一namespace下,依賴的模塊不須要引用,如define("namespace.classA",factory)再也不須要define("namespace.classA",["namespace.classB"],factory)

js中繼承直接經過冒號:define("namespace.class:base",[namespaces],factory)

js中部分類直接經過partial關鍵字define("partial namespace.class",[namespaces],factory)

ps:尼瑪!要求這麼多,那仍是js了嗎?必定要把js改爲C#同樣嗎?直接去用cs和ts算了?規範有可行性嗎?能實現嗎?

恩!js是個可塑性很強的小子,你想把他塑形成什麼形象,他就成什麼樣子。

舉個栗子

define("AppName.Song", function () {
    var Song = function (title) {
        this.title = title;
    }
})
define("AppName.Album", function () {
    var Album = {};
    Album.title = "當耐特專輯";
    Album.songs = [new Song("當耐特進行曲"), new Song("當耐特蕩起雙槳")];
})
require(["AppName"], function () {
    var span = document.createElement("span"), text = "";
    for (var i = 0, len = arguments.length; i < len; i++) {
        text += "第" + i + "個參數:" + arguments[i].toString();
        text += "<br/>"
    }

    var song = new Song("春天的故事");
    text += "song title:" + song.title;
    text += "<br/>";
    text += "album first song:" + Album.songs[0].title;
    span.innerHTML = text;
    var resultShowPanel=document.getElementById("resultShowPanel");
    resultShowPanel.innerHTML="";
    resultShowPanel.appendChild(span);
})

能夠在不一樣操做系統或瀏覽器環境測試,兼容到IE5.5+

從代碼能夠看出:

在Album中,不須要引用Song,就可使用父AppName下的Song

在程序入口require下,直接引用top namespace就可使用其下的Song和Album

原理

先看下圖:

20140616084001

拿到function以後進行toString,再重構該string,而後建立新的Function,再apply執行,把賴的模塊傳給apply的第二個參數。有碼有真相:

_findRefrence = function (deps, callback, isDefine, className, mdName) {
    var i = 0, len = deps.length, moduleArr = [], moduleNameArr = [];
    for (; i < len; i++) {
        for (var key in modules) {
            var arr = key.split("."), ns = arr[0], cl = arr[1];
            if (ns === deps[i]) {
                moduleNameArr.push(cl);
                moduleArr.push(modules[key]);
            }
        }
    }
    var entire = callback.toString();
    var body = entire.slice(entire.indexOf("{") + 1, entire.lastIndexOf("}")) + (isDefine ? ("return " + className + ";") : "");
    var fn = new Function(moduleNameArr, body);
    var obj = fn.apply(null, moduleArr);
    if (isDefine) {
        modules[mdName] = obj;
    }
}

此時該有掌聲,但且慢着鼓掌,這是第一個版本,僅僅不夠。再看下個栗子:

再舉栗子

如今能夠看到,define的function沒有了?所有成了{init:xxx,xxx:xxx}的JSON格式,require還保留了其回掉的function,這樣是符合語義的。

簡直是極簡主義!簡單就是美。但簡單的背後作了大量的工做。

原理

看圖:

20140616090652

相關代碼:

function JSONstringifyWithFuncs(obj) {
    Object.prototype.toJSON = function () {
        var sobj = {}, i;
        for (i in this)
            if (this.hasOwnProperty(i))
                sobj[i] = typeof this[i] == 'function' ?
                    this[i].toString() : this[i];

        return sobj;
    }
}

這樣,json裏面function的信息也不回丟失。

Class使用的是John Resig的Class,init爲構造函數,使用_super能夠調用父類方法很方便。

總結

有些好的東西,因爲歷史緣由可能會遭受大量的反對,但這就是我心目中,理想規範方便極簡的模塊化開發方式,後續發佈並支持腳本加載和namespace樹,如:

system

system.web

system.web.ui

system.web.ui.control

system.web.ui.control.xx.xxx.xxx.xxx……

求磚和薦。

相關文章
相關標籤/搜索