Blog

Blog

PHODAL

亚马逊1Button App源码分析之JavaScript模块化思想

我想你大概已经知道了正常情况下使用RequireJS怎么写原型函数,而亚马逊写这个原型函数,看上去就可奇怪了。

RequireJS原型函数

下面是一个简单的RequireJS风格下的原型函数的示例:

define([
    'underscore'
], function (_) {
    function func() {}

    func.prototype.init = function () {};
    return func;     
});

如果你对此很熟悉的话,应该很容易,接着我们来看看亚马逊的前端"攻城狮"是如何将C的思想用到前端上面。

Amazone RequireJS原型函数

里面基本上就是这种风格,于是在里面找到了一个文件叫做s3-client.js,于是无耻地贴出了部分的代码。

var factory = function(
    _,
    Flow,
    $lang,
    $options
) {

    "use strict";
    var Client = function() {
        this.initialize.apply(this, arguments);
    };

    Client.prototype.initialize = function(opts) {
    };

    Client.prototype.getObject = function(objectUrl, responseType, cb) {
    };

    Client.prototype._makeRequest = function(operation, opts) {
    };

    return Client;
};

if (typeof module !== "undefined" && module.exports) {
    module.exports = factory(
        require("underscore"),
        require("bit/commons/flow"),
        require("bit/commons/lang"),
        require("bit/commons/options")
    );
} else if (typeof define !== "undefined") {
    define([
        "underscore",
        "bit/commons/flow",
        "bit/commons/lang",
        "bit/commons/options"
    ], factory);
}

看上去和我们之前写的代码似乎区别很大。

CommonJS vs AMD

回到经常写的NodeJS上的module,NodeJS采用的是CommonJS的规范,简单地来说对于上面的情况,我们是这样来定义一个函数的:

function init(){
}
exports.init_func = init;

这也是在exports对象上定义一个init_func方法来供外部调用。

而在AMD中我们是这样调用的:

define([], function () {
    function init() {
    }

    return {
        init_func: init
    }
});

在《JavaScript设计模式》中有一句话很有意思: AMD是模块化JavaScript的最好选择

除此之外,应该还有混合模式。

Amazon JavaScript模块化思想

于是总结了一下上面的思想,亚马逊就是那么任性地把两者结合到了一起:

定义了一个factory函数

var factory = function(){
    var Client = function() {};
    return Client;
}

然后返回地是Client函数,于是每一个模块的开头都是factory,他们家是因为没有工厂,所以才造了那么多工厂么?

if (typeof module !== "undefined" && module.exports) {
    module.exports = factory(
     );
} else if (typeof define !== "undefined") {
    define([
     ], factory);
}

有点类似于UMD

Universal Module Definition

AMD以浏览器为第一(browser-first)的原则发展,选择异步加载模块。它的模块支持对象(objects)、函数(functions)、构造器(constructors)、字符串(strings)、JSON等各种类型的模块。因此在浏览器中它非常灵活。

CommonJS module以服务器端为第一(server-first)的原则发展,选择同步加载模块。它的模块是无需包装的(unwrapped modules)且贴近于ES.next/Harmony的模块格式。但它仅支持对象类型(objects)模块。

于是,下面是一个简单的UMD的示例,来自于《JavaScript设计模式》

(function (define) {
    define('id', function (require, exports) {
        var a = require('a');

        exports.name = value;
    });
}(typeof define === 'function' && define.amd ? define : function (id, factory) {
    if (typeof exports !== 'undefined') {
        factory(require, exports);
    } else {
        factory(function(value) {
            return window[value];
        }, (window[id] = {}));
    }
}));

在UnderScore中我们是这样来封装函数的:

if (typeof exports !== 'undefined') {
    if (typeof module !== 'undefined' && module.exports) {
        exports = module.exports = _;
    }
    exports._ = _;
} else {
    root._ = _;
}

if (typeof define === 'function' && define.amd) {
    define('underscore', [], function() {
        return _;
    });
}

而这种方式又何尝不是一种UMD呢?

结论

不同的人总会用不同的思想去解决问题。

关于我

Github: @phodal     微博:@phodal     知乎:@phodal    

微信公众号(Phodal)

围观我的Github Idea墙, 也许,你会遇到心仪的项目

QQ技术交流群: 321689806
comment

Feeds

RSS / Atom

最近文章

关于作者

Phodal Huang

Engineer, Consultant, Writer, Designer

ThoughtWorks 技术专家

工程师 / 咨询师 / 作家 / 设计学徒

开源深度爱好者

出版有《前端架构:从入门到微前端》、《自己动手设计物联网》、《全栈应用开发:精益实践》

联系我: h@phodal.com

微信公众号: 最新技术分享

标签