CMD(Common Module Definition,通用模块定义)是一种模块定义规范,规范中明确了模块的基本书写格式和基本交互规则。SeaJS就是遵循的这个规范。

define函数

在CMD规范中,一个模块就是一个文件,模块的区分通过define关键字来定义,最基本的格式是:

define(factory); // 这里的define是一个全局函数,factory可以是函数、对象或字符串

define接受factory参数,factory可以是一个函数,也可以是一个对象或字符串。

当factory为对象、字符串时,表示模块的接口就是该对象、字符串。

比如可以如下定义一个JSON数据模块:

define({ "foo": "bar" });

也可以通过字符串定义模板模块:

define('I am a template. My name is {{name}}.');

当factory为函数时,表示是模块的构造方法。执行该构造方法,可以得到模块向外提供的接口。

当factory方法在执行时,默认会传入三个参数:require、exports和module:

define(function(require, exports, module) {
// 模块代码
});

define也可以接受两个以上的参数,进阶的格式是:

define(id, deps, factory); // id是模块标识字符串,deps是模块依赖数组

参数中,字符串id表示模块标注,数组deps表示模块依赖。

define('helloworld', ['jquery'], function(require, exports, module) {
// 模块代码
});

id和deps参数是可以省略的,省略时可以通过构建工具自动生成。注意的是带id和deps参数的define用法不属于CMD规范,而属于Modules/Transport规范。

define函数带有成员属性cmd,是一个空对象,可用来判断当前页面是否有CMD模块加载器。

if (typeof define === "function" && define.cmd) {
// 有 Sea.js 等 CMD 模块加载器存在时的逻辑代码
}

require函数(factory函数的参数之一)

require是factory函数的第一个参数,它有下列的用法:

require(id),通过接受模块标识作为唯一参数,用来获取其他模块提供的接口。

define(function(require, exports) {
// 获取模块 a 的接口
var a = require('./helloworlda'); // 调用模块 helloworld 的方法
a.doSomething();
});

require.async(id, callback),用来在模块内部加载模块,并在加载完成后执行指定回调。callback参数可选。

define(function(require, exports, module) {
// 异步加载一个模块,在加载完成时,执行回调
require.async('./b', function(b) {
b.doSomething();
}); // 异步加载多个模块,在加载完成时,执行回调
require.async(['./c', './d'], function(c, d) {
c.doSomething();
d.doSomething();
});
});

与上一个用法不同的是,前者是同步往下执行,后者是异步回调执行,一般用来加载可延迟异步加载的模块。

require.resolve(id),使用模块系统内部的路径解析机制来解析并返回模块路径,该函数并不会加载模块,只返回解析后的绝对路径。

define(function(require, exports) {
console.log(require.resolve('./b'));// http://path/b.js
});

这样就可以获取模块的路径,一般用在插件环境或需要动态拼接模块路径的场景下。

exports对象(factory函数的参数之一)

exports是一个对象,用来向外提供模块接口。

define(function(require, exports) {
// 对外提供 foo 属性
exports.foo = 'bar'; // 对外提供 doSomething 方法
exports.doSomething = function() {};
});

除了给exports对象增加成员,还可以使用return直接向外提供接口。

define(function(require) {
// 通过 return 直接提供接口
return {
foo: 'bar',
doSomething: function() {}
};
});

如果return语句是模块中的唯一代码,还可以简化为(factory为对象或字符串):

define({
foo: 'bar',
doSomething: function() {}
});

上面这种格式特别适合定义一个JSONP模块。

要注意的是,exports仅仅是module.exports的一个引用,在factory内部给exports重新赋值时,并不会改变module.exports的值,因此给exports赋值是无效的,不能用来更改模块接口。比如以下的写法就是错误的:

define(function(require, exports) {
// 错误用法!!!
exports = { // 应该改为module.exports = {}
foo: 'bar',
doSomething: function() {}
};
});

module对象(factory函数的参数之一)

module是一个对象,存储与当前模块相关的一些属性和方法。

module.id,模块的唯一标识。

define('yanggb', [], function(require, exports, module) {
// 模块代码
console.log(module.id); // yanggb
});

define函数的第一个参数就是模块唯一标识,用module取得时候就能得到yanggb。

module.uri,模块的绝对路径,根据模块系统的路径解析规则得到。

define(function(require, exports, module) {
// 模块代码
console.log(module.uri); // http://path/file.js
});

一般情况下(没有在define函数中自定义id参数),module.id的值就是module.uri的值。

module.dependencies,模块的依赖,是一个数组。

module.exports,模块对外提供的接口。

传给factory函数的exports参数是module.exports对象的一个引用。只通过exports参数来提供接口,有时无法满足开发者的所有需求。例如当模块的接口是某个类的实例时,就需要通过module.exports来实现:

define(function(require, exports, module) {
// exports 是 module.exports 的一个引用
console.log(module.exports === exports); // true // 重新给 module.exports 赋值
module.exports = new SomeClass(); // exports 不再等于 module.exports
console.log(module.exports === exports); // false
});

要注意的是,对module.exports的赋值必须要同步执行,不能放到回调函数里(异步)。

// x.js
define(function(require, exports, module) {
// 错误用法
setTimeout(function() {
module.exports = {a: "hello"};
}, 0);
}); // y.js,在 y.js 里有调用到上面的 x.js:
define(function(require, exports, module) {
var x = require('./x'); // 无法立刻得到模块 x 的属性 a (有延迟)
console.log(x.a); // undefined
});

总结

这里学习的顺序就是:define函数 -> factory函数(define函数的参数) -> require函数、exports对象和modules对象(factory函数的参数)。其实用法也比较简单,对开发很有帮助,有助于开发思路的整理和后期代码的维护。优点也就是模块化编程的优点。

"我唱的不够动人你别皱眉。"

javascript模块化编程的cmd规范(sea.js)的更多相关文章

  1. Javascript模块化编程(二)AMD规范(规范使用模块)

    这个系列的第一部分介绍了Javascript模块的基本写法,今天介绍如何规范地使用模块,先想一想,为什么模块很重要?接下来为您详细介绍,感兴趣的朋友可以了解下啊.今天介绍如何规范地使用模块. 七.模块 ...

  2. Javascript模块化编程:AMD规范及require.js用法【转】 - loheonly的笔记 - 前端网(W3Cfuns)

    http://www.w3cfuns.com/blog-5425789-5399326.html

  3. 坑人的 Javascript 模块化编程 sea.js

    坑人的 Javascript 模块化编程 sea.js 忧伤 加 蛋疼的 开始了 看文档 Sea.js 进行配置 seajs.config({ // 设置路径,方便跨目录调用 paths: { 'ar ...

  4. javascript模块化编程思想、实现与规范

    随着BS架构的发展,网站逐渐变成了互联网应用程序,嵌入网络的JavaScript代码越来越庞大,越来越复杂(业务逻辑处理或用户交互很多写在前端).网页越来越像桌面程序,需要一个团队分工协作.进度管理. ...

  5. Javascript模块化编程(三):require.js的用法

    Javascript模块化编程(三):require.js的用法 原文地址:http://www.ruanyifeng.com/blog/2012/11/require_js.html 作者: 阮一峰 ...

  6. Javascript模块化编程(二):AMD规范

    Javascript模块化编程(二):AMD规范   作者: 阮一峰 原文地址:http://www.ruanyifeng.com/blog/2012/10/asynchronous_module_d ...

  7. Javascript模块化编程(二):AMD规范(转)

    这个系列的第一部分介绍了Javascript模块的基本写法,今天介绍如何规范地使用模块. (接上文) 七.模块的规范 先想一想,为什么模块很重要? 因为有了模块,我们就可以更方便地使用别人的代码,想要 ...

  8. Javascript模块化编程(二):AMD规范 作者: 阮一峰

    声明:转载自阮一峰的网络日志 这个系列的第一部分介绍了Javascript模块的基本写法,今天介绍如何规范地使用模块. (接上文) 七.模块的规范 先想一想,为什么模块很重要? 因为有了模块,我们就可 ...

  9. Javascript模块化编程之路——(require.js)

    转自:http://www.ruanyifeng.com/blog/2012/10/javascript_module.html Javascript模块化编程(一):模块的写法 随着网站逐渐变成&q ...

随机推荐

  1. Django之Django简介,开发环境搭建,项目应用创建

    软件及Django框架简介 软件框架 一个软件框架是由其中各个软件模块组成的: 每一个模块都有特定的功能: 模块与模块之间通过相互配合来完成软件的开发. 软件框架是针对某一类软件设计问题而产生的. M ...

  2. java基础(20):Map、可变参数、Collections

    1. Map接口 1.1 Map接口概述 我们通过查看Map接口描述,发现Map接口下的集合与Collection接口下的集合,它们存储数据的形式不同,如下图. Collection中的集合,元素是孤 ...

  3. PlayJava Day005

    今日所学: /* 2019.08.19开始学习,此为补档. */ 类:一类事物的抽象体(如全人类,学生类,订单类) 对象:具体的个体(如张三,某个外卖订单) 对象具有属性和行为. 声明的属性语句一般放 ...

  4. 高强度学习训练第九天总结:5道剑指offer的题目

    实在不想看JVM了.刷几道剑指Offer的题,今天就水一水吧,脑子迷糊. 1.二维数组中的查找 在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增 ...

  5. 【C#】学习笔记(2)委托Delegate相关

    泛型委托类型,同样是根据杨老师的视频来的. 直接上栗子

  6. QML::Rectangle组件

    QML的Rectangle组件,描绘一个矩形,一个可视化的对象. 外加设置属性来达到我们想要的效果.常用的有矩形的颜色,边框颜色,圆角等设置. Rectangle{ x:10//这里的坐标是相对于它的 ...

  7. [视频教程] 使用composer安装使用thinkphp6.0框架

    安装composer -vvv的参数是表示展示安装进度,测试时使用其他参数安装失败,一直卡着不动curl -vvv https://getcomposer.org/installer | phpmv ...

  8. 7.3 DStream操作

    一.Spark Streaming工作机制 在Spark Streaming中,会有一个组件Receiver,作为一个长期运行的task跑在一个Executor上: 每个Receiver都会负责一个i ...

  9. 【bzoj4559】[JLoi2016]成绩比较(dp+拉格朗日插值)

    bzoj 题意: 有\(n\)位同学,\(m\)门课. 一位同学在第\(i\)门课上面获得的分数上限为\(u_i\). 定义同学\(A\)碾压同学\(B\)为每一课\(A\)同学的成绩都不低于\(B\ ...

  10. 【洛谷P4589】[TJOI2018]智力竞赛(二分+最小链覆盖)

    洛谷 题意: 给出一个\(DAG\),现在要选出\(n+1\)条可相交的链来覆盖,最终使得未被覆盖的点集中,权值最小的点的权值最大. 思路: 显然最终的答案具有单调性,故直接二分答案来判断: 直接将小 ...