Blog | Phodal - A Growth Engineerhttp://www.phodal.com/blog/2014-12-24T06:25:32.372972+00:00Blog亚马逊1Button App源码分析之JavaScript模块化思想2014-12-10T13:55:21+00:002014-12-24T06:25:32.372972+00:00Phodal Huanghttp://www.phodal.com/blog/author/root/http://www.phodal.com/blog/use-universal-module-definition-on-amazon-one-button-app/我想你大概已经知道了正常情况下使用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呢?
##结论
不同的人总会用不同的思想去解决问题。