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

新书《前端架构:从入门到微前端》

《前端架构:从入门到微前端》是一本围绕前端架构的实施手册,从基础的架构规范,到如何设计前端架构,再到采用微前端架构拆分复杂的前端应用。本书通过系统地介绍前端架构世界的方方面面,来帮助前端工程师更好地进行系统设计。

前端架构包含以下五部分内容:

  • 设计:讲述了架构设计的模式,以及设计和制定前端工作流。
  • 基础:通过深入构建系统、单页面应用原理、前端知识体系等,来构建出完整的前端应用架构体系。
  • 实施:通过与代码结构的方式,介绍如何在企业级应用中实施组件化架构、设计系统和前后端分离架构。
  • 微前端:引入6种微前端的概念,以及如何划分、设计微前端应用,并展示了如何实现这6种微前端架构。
  • 演进:提出更新、迁移、重构、重写、重新架构等架构演进方式,来帮助开发人员更好地设计演进式架构。
comment

Feeds

RSS / Atom

最近文章

关于作者

Phodal Huang

Developer, Consultant, Writer, Designer

ThoughtWorks 高级咨询师

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

开源深度爱好者

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

联系我: h@phodal.com

微信公众号: 与我沟通

标签