如何正确理解javascript的模块化


Posted in Javascript onMarch 02, 2017

模块化在项目中十分的重要,一个复杂的项目肯定有很多相似的功能模块,如果每次都需要重新编写模块肯定既费时又耗力。但是引用别人编写模块的前提是要有统一的“打开姿势”,如果每个人有各自的写法,那么肯定会乱套,下面介绍几种JS的模块化的规范。

一:模块化进程一:script标签

这是最原始的 JavaScript 文件加载方式,如果把每一个文件看做是一个模块,那么他们的接口通常是暴露在全局作用域下,也就是定义在 window 对象中,不同模块的接口调用都是一个作用域中,一些复杂的框架,会使用命名空间的概念来组织这些模块的接口。

缺点:

1、污染全局作用域

2、开发人员必须主观解决模块和代码库的依赖关系

3、文件只能按照script标签的书写顺序进行加载

4、在大型项目中各种资源难以管理,长期积累的问题导致代码库混乱不堪

二:模块化进程二:CommonJS规范

该规范的核心思想是允许模块通过require方法来同步加载所要依赖的其他模块,然后通过 exports 或 module.exports 来导出需要暴露的接口。

require("module");
require("../file.js");
exports.doStuff = function(){};
module.exports = someValue;

优点:

1、简单并容易使用

2、服务器端模块便于重用

缺点:

1、同步的模块加载方式不适合在浏览器环境中,同步意味着阻塞加载,浏览器资源是异步加载的

2、不能非阻塞的并行加载多个模块

module.exports与exports的区别

1、exports 是指向的 module.exports 的引用

2、module.exports 初始值为一个空对象 {},所以 exports 初始值也是 {}

3、require() 返回的是 module.exports 而不是 exports

exports示例:

// app.js
var circle = require('./circle');
console.log(circle.area(4));
// circle.js
exports.area = function(r){
 return r * r * Math.PI;
}

module.exports示例:

// app.js
var area = require('./area');
console.log(area(4));
// area.js
module.exports = function(r){
 return r * r * Math.PI;
}

错误的情况:

// app.js
var area = require('./area');
console.log(area(4));
// area.js
exports = function(r){
 return r * r * Math.PI;
}

其实是对 exports 进行了覆盖,也就是说 exports 指向了一块新的内存(内容为一个计算圆面积的函数),也就是说 exports 和 module.exports 不再指向同一块内存,也就是说此时 exports 和 module.exports 毫无联系,也就是说 module.exports 指向的那块内存并没有做任何改变,仍然为一个空对象{},也就是说area.js导出了一个空对象,所以我们在 app.js 中调用 area(4) 会报 TypeError: object is not a function 的错误。

总结:当我们想让模块导出的是一个对象时, exports 和 module.exports 均可使用(但 exports 也不能重新覆盖为一个新的对象),而当我们想导出非对象接口时,就必须也只能覆盖 module.exports 。

三:模块化进程三:AMD规范

由于浏览器端的模块不能采用同步的方式加载,会影响后续模块的加载执行,因此AMD(Asynchronous Module Definition异步模块定义)规范诞生了。

AMD标准中定义了以下两个API

1、require([module], callback);
2、define(id, [depends], callback);

require接口用来加载一系列模块,define接口用来定义并暴露一个模块。

示例:

define("module", ["dep1", "dep2"], function(d1, d2){
 return someExportedValue;
});
require(["module", "../file"], function(module, file){ /* ... */ });

优点:

1、适合在浏览器环境中异步加载模块

2、可以并行加载多个模块

缺点:

1、提高了开发成本,代码的阅读和书写比较困难,模块定义方式的语义不顺畅

2、不符合通用的模块化思维方式,是一种妥协的实现

四:模块化进程四:CMD规范

CMD(Common Module Definition)规范和AMD很相似,尽量保持简单,并与CommonJS和Node.js的 Modules 规范保持了很大的兼容性。在CMD规范中,一个模块就是一个文件。

示例:

define(function(require, exports, module){
 var $ = require('jquery');
 var Spinning = require('./spinning');
 exports.doSomething = ...
 module.exports = ...
})

优点:

1、依赖就近,延迟执行

2、可以很容易在 Node.js 中运行

缺点:

1、依赖 SPM 打包,模块的加载逻辑偏重

AMD和CMD的区别

AMD和CMD起来很相似,但是还是有一些细微的差别,让我们来看一下他们的区别在哪里:

1、对于依赖的模块,AMD是提前执行,CMD是延迟执行。

2、AMD推崇依赖前置;CMD推崇依赖就近,只有在用到某个模块的时候再去require。看代码:

// AMD
define(['./a', './b'], function(a, b){ // 依赖必须一开始就写好
  a.doSomething()  
  // 此处略去 100 行
  b.doSomething()  
  ...
});
// CMD
define(function(require, exports, module){
  var a = require('./a')  
  a.doSomething()  
  // 此处略去 100 行
  var b = require('./b')
  // 依赖可以就近书写
  b.doSomething()
  // ...
});

3、AMD 的 API 默认是一个当多个用,CMD 的 API 严格区分,推崇职责单一。

五:模块化进程五:ES6模块化

EcmaScript6标准增加了JavaScript语言层面的模块体系定义。ES6 模块的设计思想,是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS和AMD模块,都只能在运行时确定这些东西。

在 ES6 中,我们使用export关键字来导出模块,使用import关键字引用模块。需要说明的是,ES6的这套标准和目前的标准没有直接关系,目前也很少有JS引擎能直接支持。因此Babel的做法实际上是将不被支持的import翻译成目前已被支持的require。

尽管目前使用import和require的区别不大(本质上是一回事),但依然强烈推荐使用import关键字,因为一旦JS引擎能够解析ES6的import关键字,整个实现方式就会和目前发生比较大的变化。如果目前就开始使用import关键字,将来代码的改动会非常小。

示例:

import "jquery";
export functiondoStuff(){}
module "localModule" {}

优点:

1、容易进行静态分析

2、面向未来的 EcmaScript 标准

缺点:

1、原生浏览器端还没有实现该标准

2、全新的命令字,新版的 Node.js才支持

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持三水点靠木!

Javascript 相关文章推荐
js修改table中Td的值(定义td的双击事件)
Jan 10 Javascript
javascript实现十六进制颜色值(HEX)和RGB格式相互转换
Jun 20 Javascript
JavaScript lastIndexOf方法入门实例(计算指定字符在字符串中最后一次出现的位置)
Oct 17 Javascript
老生常谈JavaScript数组的用法
Jun 10 Javascript
js获取地址栏中传递的参数(两种方法)
Feb 08 Javascript
详解vue-router 路由元信息
Sep 13 Javascript
不到200行 JavaScript 代码实现富文本编辑器的方法
Jan 03 Javascript
JS实现的点击按钮图片上下滚动效果示例
Jan 28 Javascript
jQuery实现经典的网页3D轮播图封装功能【附源码下载】
Feb 15 jQuery
微信小程序把百度地图坐标转换成腾讯地图坐标过程详解
Jul 10 Javascript
详细分析Node.js 模块系统
Jun 28 Javascript
JS前端监控采集用户行为的N种姿势
Jul 23 Javascript
jquery实现左右滑动式轮播图
Mar 02 #Javascript
如何写好你的JavaScript【推荐】
Mar 02 #Javascript
js前端日历控件(悬浮、拖拽、自由变形)
Mar 02 #Javascript
JS操作input标签属性checkbox全选的实现代码
Mar 02 #Javascript
JavaScript正则获取地址栏中参数的方法
Mar 02 #Javascript
原生js仿浏览器滚动条效果
Mar 02 #Javascript
使用grunt合并压缩js和css文件的方法
Mar 02 #Javascript
You might like
PHP生成网页快照 不用COM不用扩展.
2010/02/11 PHP
php批量上传的实现代码
2013/06/09 PHP
使用php测试硬盘写入速度示例
2014/01/27 PHP
php设置允许大文件上传示例代码
2014/03/10 PHP
thinkPHP数据库增删改查操作方法实例详解
2016/12/06 PHP
PHP实现数据库的增删查改功能及完整代码
2018/04/18 PHP
thinkPHP5.0框架事务处理操作简单示例
2018/09/07 PHP
PHP生成指定范围内的N个不重复的随机数
2019/03/18 PHP
Gambit vs CL BO3 第一场 2.13
2021/03/10 DOTA
js实现GridView单选效果自动设置交替行、选中行、鼠标移动行背景色
2010/05/27 Javascript
js异常捕获方法介绍
2013/04/10 Javascript
基于OO的动画附加插件,可以实现弹跳、渐隐等动画效果 分享
2013/06/24 Javascript
jQuery中的pushStack实现原理和应用实例
2015/02/03 Javascript
jquery控制背景音乐开关与自动播放提示音的方法
2015/02/06 Javascript
JavaScript hasOwnProperty() 函数实例详解
2017/08/04 Javascript
BootStrap模态框和select2合用时input无法获取焦点的解决方法
2017/09/01 Javascript
swiper自定义分页器使用方法详解
2020/09/14 Javascript
浅谈vue加载优化策略
2019/03/19 Javascript
利用Vue-draggable组件实现Vue项目中表格内容的拖拽排序
2019/06/07 Javascript
[06:24]DOTA2 2015国际邀请赛中国区预选赛第二日TOP10
2015/05/27 DOTA
利用Python生成文件md5校验值函数的方法
2017/01/10 Python
python微信跳一跳游戏辅助代码解析
2018/01/29 Python
Python多线程中阻塞(join)与锁(Lock)使用误区解析
2018/04/27 Python
Python爬虫设置代理IP(图文)
2018/12/23 Python
python字典改变value值方法总结
2019/06/21 Python
xadmin使用formfield_for_dbfield函数过滤下拉表单实例
2020/04/07 Python
用于ETL的Python数据转换工具详解
2020/07/21 Python
Python 常用日期处理 -- calendar 与 dateutil 模块的使用
2020/09/02 Python
python中用ggplot绘制画图实例讲解
2021/01/26 Python
Biblibili视频投稿接口分析并以Python实现自动投稿功能
2021/02/05 Python
瑞典时尚服装购物网站:Miinto.se
2017/10/30 全球购物
简历中求职的个人自我评价
2013/12/03 职场文书
大学生党员批评与自我批评范文
2014/10/14 职场文书
离婚协议书范文2014
2014/10/16 职场文书
狮子林导游词
2015/02/03 职场文书
Python作用域和名称空间的详细介绍
2022/04/13 Python