专栏首页IMWeb前端团队AMD and CMD are dead之js模块化黑魔法

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

缘由

在2013-03-06 13:58的时候,曾甩下一片文章叫:《为什么不使用requirejs和seajs》,并放下豪言说发布一款完美的模块化库,再后来就把那篇文章删了,再然后就没有然后。该用seajs还用seajs,甚至我码的SCJ都是用requirejs组织起来的。

时光飞逝,岁月流转。弹指间,来到了2014年6月15日,也就是昨日,突然码兴大发,一发不可收拾,也许跟最近小说和诗写得比较猛,把码意给压抑了,便有了这次喷发。

js问题

作为一名前MS必应团队资深当耐特(.NET)石专家,拿js与C#开发应用开发做个对比,js主要暴露的问题有:

1.没有class关键字来定义类 2.没有namespace关键字来定义命名空间 3.没有using/require/import/include关键字来处理依赖 4.继承、partial class、static、private、protected、publish等都要通过小技巧或者特定约定规范且手段太多

AMD和CMD的问题

为什么要define(function(){return xx})?

为什么要本是同根生,还要deps?

为什么要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 = text;
    var resultShowPanel=document.getElementById("resultShowPanel");
    resultShowPanel="";
    resultShowPanel.appendChild(span);
})

可以在不同操作系统或浏览器环境测试,兼容到IE5.5+

从代码可以看出:

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

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

原理

先看下图:

拿到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,这样是符合语义的。

简直是极简主义!简单就是美。但简单的背后做了大量的工作。

原理

看图:

相关代码:

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……

求砖和荐。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

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

    在2013-03-06 13:58的时候,曾甩下一片文章叫:《为什么不使用requirejs和seajs》,并放下豪言说发布一款完美的模块化库,再后来就把那篇文...

    IMWeb前端团队
  • Babel下的ES6兼容性与规范

    ES6标准发布后,前端人员也开发渐渐了解到了es6,但是由于兼容性的问题,仍然没有得到广泛的推广,不过业界也用了一些折中性的方案来解决兼容性和开发体系问题,但大...

    IMWeb前端团队
  • Babel下的ES6兼容性与规范

    ES6标准发布后,前端人员也开发渐渐了解到了es6,但是由于兼容性的问题,仍然没有得到广泛的推广,不过业界也用了一些折中性的方案来解决兼容性和开发体系问题,但大...

    IMWeb前端团队
  • AMD and CMD are dead之js模块化黑魔法

    在2013-03-06 13:58的时候,曾甩下一片文章叫:《为什么不使用requirejs和seajs》,并放下豪言说发布一款完美的模块化库,再后来就把那篇文...

    IMWeb前端团队
  • Babel下的ES6兼容性与规范

    ES6标准发布后,前端人员也开发渐渐了解到了es6,但是由于兼容性的问题,仍然没有得到广泛的推广,不过业界也用了一些折中性的方案来解决兼容性和开发体系问题,但大...

    IMWeb前端团队
  • Babel下的ES6兼容性与规范

    ES6标准发布后,前端人员也开发渐渐了解到了es6,但是由于兼容性的问题,仍然没有得到广泛的推广,不过业界也用了一些折中性的方案来解决兼容性和开发体系问题,但大...

    IMWeb前端团队
  • 从C#到TypeScript - Generator

    从C#到TypeScript - Generator 上篇讲了Promise,Promise的执行需要不停的调用then,虽然比callback要好些,但也显得...

    用户1147588
  • 使用 JavaScript 自动化你的 Mac

    系统自带的编写自动化脚本的工具是ScriptEditor。打开编辑器,把语言从 AppleScript切换到JavaScritp。接下来我们就来实战一下,写一个...

    IMWeb前端团队
  • 几个会被面试问到的JS基础实现代码

    记录工作和学习中的一些可能会被面试问到的JS内部实现基础,由于笔者水平有限,提供出的答案不一定准确,但是原理尽可能会讲清楚,以后会进行逐步添加。 bind实现 ...

    糊糊糊糊糊了
  • 性能优化三部曲之三——Node直出让你的网页秒开

    项目: 手Q群成员分布直出 原因: 为家校群业务直出做准备 群成员分布业务是小型业务,而且逻辑相当简单,方便做直出试验田 基本概念: 直出其实并不算是新概念。只...

    李成熙heyli

扫码关注云+社区

领取腾讯云代金券