不知道有多少同窗像我同样写了好久的JS代码😅,可是对JS的模块化仍是不清楚,这篇文章比较适合新手朋友,通读以后能对模块化有一个简单的概念。前端
模块化,其实就是在项目中定义若干个相对独立的可复用的模块,模块中包含特定的功能,当你想要用这个功能的时候,只须要引入对应的模块便可。npm
其实模块化主要是为了规避和全局属性的冲突,同时也但愿写模块的时候能本身维护本身的模块,多人项目开发的时候不用担忧和别人变量冲突。编程
模块化其实也就是某种意义上面的全局,或者和封装代码,只不过是封装的稍微好一点而已,其实也不是全局变量。设计模式
在早期的时代,若是咱们想要导出一个模块经常使用的方法就是经过闭包,加持一点设计模式来实现模块化,比较经典的就是JQuery实现模块的方式:前端工程化
(function(window) {
/// 代码
window.jQuery = window.$ = jQuery; // 经过给window添加属性而暴露到全局
})(window)
复制代码
这种封装模式被不少人模仿,经过匿名函数包装代码,所依赖的外部变量传给这个函数,这个函数内部可使用这些依赖,而后再将模块自身暴露给window。浏览器
虽然这种模式看似很方便,可是其实仍是增长了全局变量,并且当咱们要引用的时候也一样须要注意引用模块的顺序,否则极可能致使页面报错。bash
这部分因为年代久远,并且如今在项目基本不多会使用,因此这里做者就只把一些浅显的知识点加以整理,算是简单的了解一下吧。
AMD和CMD主要是解决在浏览器端的异步模块化编程的需求,其中AMD和CMD最具表明的两个做品分别是require.js和sea.js,有兴趣的能够详细了解一下。闭包
这二者用法基本相同,区别就是AMD崇尚依赖前置,CMD崇尚的是按需加载,也就是在须要用到某个模块的时候再进行加载。异步
defined('模块名称', ['依赖的模块1', '依赖的模块2', ], function (moudle1, moudle2) {
class item {}
return item 或者 module.exports = item
});
复制代码
const moudle = require('./moudle');
复制代码
这里须要注意的是,若是当前文件是AMD一个模块能够像下面这样写:模块化
defined('test', function () {
var a = require('a'); // 按需加载,在用到的地方在加载
});
复制代码
若是不是,按照AMD规范须要定义成局部require
var a = require(['a'], function () {
代码
});
复制代码
由于这里require多是一个异步的形式,这是就须要用回调,若是直接var a = require('a');这样使用也成功了的话,只能证实这个模块加载或定义过,不过在全局的话最稳妥的方式仍是上面的那种形式。
能够思考一下当咱们在用的时候:
var utils = require('./utils');
utils.xxx();
复制代码
这样写的话,JS指定是来不及加载的,可是这个语句是同步执行的,他们是怎么作的呢????
其实他会先把咱们的函数先执行一下toString方法,拿到函数的字符串,而后用正则一下,看看哪里用到了require
var functionStr = function.toString();
var regx = /require\(['"][^)*]['"]\)/ 复制代码
这样就能够拿到咱们项目中须要require的模块,而后就能够在使用以前进行加载模块了。
2009 年 ry 发布 Node.js 的第一个版本,CommonJS 做为其中最核心的特性之一,适用于服务端下的场景;历年来的考察和时间的洗礼,以及前端工程化对其的充分支持,CommonJS 被普遍运用于 Node.js 和浏览器:
注意:这里不要直接修改exports的值
// a.js
console.log(require('./b')); // {}
// b.js
exports = { name: 'changan' }
复制代码
上面代码能够看出,这样用的话咱们require的是一个空对象,这是为何呢?
咱们能够经过exports的伪代码来看一下:
// 当咱们要导出模块的时候,内核帮忙声明
var exports = moudle.exports = { };
复制代码
想象一下,当咱们进行例子中的操做的时候,至关于咱们会把exports变量覆盖掉,可是moudle.exports并无改变,当咱们require模块的时候仍是会找module.exports对象,因此咱们会拿到一个空对象,这部分须要注意一下。
//a.js
var obj = {
name: 1
}
module.exports = {
getName: function () {
return obj.name
},
setName: function (name) {
obj.name = name;
}
}
// ==================
// b.js
var a = require('./a');
console.log('a-1: ', a.getName()); // a-1:1
a.setName(2222);
console.log('a-2: ', a.getName()); // a-2:2222
// ==================
// c.js
var a = require('./a');
var b = require('./b');
console.log('a-3: ', a.getName()); // a-3:2222
复制代码
这里仍是值得注意一下的,由于commonJS引入的模块是单例的,因此这也就解释了为何当两个文件引用同一个模块以后,一个文件改变了这个模块的值的时候,另外一个模块的值也是会改变。在开发中经常会由于不知道这个原理而焦头烂额,当一个同事改变了模块的一个值的时候,另外一个引用该模块的地方也会改变,避免的最好办法就是不要更改模块内部的值。
ES Module 是语言层面的模块化方案,由 ES 2015 提出,其规范与 CommonJS 比之 ,导出的值均可以当作是一个具有多个属性或者方法的对象,能够实现互相兼容。
// a.js
export let a = 1;
export function caculate() {
a++;
};
// b.js
import { a, caculate } from 'a.js';
console.log(a); // 1
caculate();
console.log(a); // 2
a = 2; // Syntax Error: "a" is read-only
复制代码