你是如何理解js中的??榛模?/h1>

一、什么是??榛?/strong>

在js出现的时候,js一般只是用来实现一些简单的交互,后来js开始得到重视,用来实现越来越复杂的功能,而为了维护的方便,我们也把不同功能的js抽取出来当做一个js文件,但是当项目变的复杂的时候,一个html页面可能需要加载好多个js文件,而这个时候就会出现各种命名冲突,如果js也可以像java一样,把不同功能的文件放在不同的package中,需要引用某个函数或功能的时候,import下相关的包,这样可以很好的解决命名冲突等各种问题,但是js中没有模块的概念,又怎么实现模块化呢

??榛⑹且恢止芾矸绞?,是一种生产方式,一种解决问题的方案,一个??榫褪鞘迪痔囟üδ艿奈募?,有了???,我们就可以更方便地使用别人的代码,想要什么功能,就加载什么???,但是??榭⑿枰裱欢ǖ墓娣?,否则就都乱套了,因此,才有了后来大家熟悉的AMD规范,CMD规范

接下来,我们就一起学习下AMD,CMD和es6中的??榛?/p>

二、AMD

AMD 即Asynchronous Module Definition,中文名是“异步模块定义”的意思,它采用异步方式加载???,??榈募釉夭挥跋焖竺嬗锞涞脑诵?,所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行

一般来说,AMD是 RequireJS 在推广过程中对模块定义的规范化的产出,因为平时在开发中比较常用的是require.js进行??榈亩ㄒ搴图釉?,一般是使用define来定义???,使用require来加载模块

1、定义模块

AMD规范只定义了一个函数define,它是全局变量,我们可以用它来定义一个模块

define(id?, dependencies?, factory);

其中,id是定义中??榈拿?,这个参数是可选的,如果没有提供该参数,模块的名字应该默认为??榧釉仄髑肭蟮闹付ń疟镜拿郑绻峁┝烁貌问?,模块名必须是“顶级”的和绝对的

dependencies是定义的??橹兴览的?榈氖?,依赖??楸匦敫菽?榈墓こХ椒ㄓ畔燃吨葱校⑶抑葱械慕峁Ω冒凑找览凳橹械奈恢盟承蛞圆问男问酱耄ǘㄒ逯心?榈模┕こХ椒ㄖ?/p>

factory是??槌跏蓟葱械暮蚨韵?,如果为函数,它应该只被执行一次,如果是对象,此对象应该为模块的输出值

下面来看一个定义??榈睦?/p>

define("alpha", ["require", "exports", "beta"], function (require, exports, beta) {
     exports.verb = function() {
         return beta.verb();
         //Or:
         return require("beta").verb();
     }
});

上面的代码定义了一个alpha的???,这个模块依赖require,exports,beta,因此需要先加载它们,再执行后面的factory

2、加载模块

require.js中采用require()语句加载???,在定义好了??楹?,我们可以使用require进行??榈募釉?/p>

require([module], callback);

require要传入两个参数,第一个参数[module],是一个数组,里面的成员就是要加载的???,第二个参数callback,则是加载成功之后的回调函数

下面我们来看一个例子

require([increment'], function (increment) {
    increment.add(1);
});

上面的代码中,比如我们现在已经定义了一个???,名字为increment,里面有一个add方法,我们现在需要用到里面的方法,只要像上面一样将模块加载进来,然后调用方法就可以了

3、requirejs使用例子

在使用require.js时,可以通过define()定义模块,这时候里面的模块的方法和变量外部是无法访问到的,只有通过return,然后再加载这个模块,才可以进行访问

define('math',['jquery'], function ($) {//引入jQuery???    return {
        add: function(x,y){
            return x + y;
        }
    };
});

上面的代码定义了一个math模块,返回了一个add方法,要使用这个??榈姆椒ǎ颐切枰蛳旅嬲庋蟹梦?/p>

require(['jquery','math'], function ($,math) {
    console.log(math.add(10,100));//110
});

通过require,我们加载了math模块,这样就可以使用math模块里面的add方法了

三、CMD

CMD 即Common Module Definition通用??槎ㄒ?,CMD规范是国内发展出来的,同时,CMD是在SeaaJS推广的过程中形成的,CMD和AMD要解决的都是同个问题,在使用上也都很像,只不过两者在模块定义方式和??榧釉厥被嫌兴煌?/strong>

1、定义???/strong>

在 CMD 规范中,一个??榫褪且桓鑫募ü齞efine()进行定义

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) {

  // ??榇?
});

其中,require用来加载其它???,而exports可以用来实现向外提供模块接口

define(function(require, exports) {

  // 对外提供 foo 属性
  exports.foo = 'bar';

  // 对外提供 doSomething 方法
  exports.doSomething = function() {};

});

module是一个对象,上面存储了与当前模块相关联的一些属性和方法,传给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

});

说了这么多,相信大家可能有点乱,来个简单的例子,我们看看使用AMD和CMD定义的??榈男捶?/p>

// CMD
define(function(require, exports, module) {
  var a = require('./a')
  a.doSomething()
  // 此处略去 100 行
  var b = require('./b') // 依赖可以就近书写
  b.doSomething()
  // ... 
})

// AMD 默认推荐的是
define(['./a', './b'], function(a, b) { // 依赖必须一开始就写好
  a.doSomething()
  // 此处略去 100 行
  b.doSomething()
  ...
})

在上面的代码中,相信大家很容易可以看出区别吧,AMD和CMD都是通过define()定义??椋珹MD需要把依赖的??橄刃闯隼?,可以通过return暴露接口,CMD在定义??樾枰雛equire,exports和module这几个参数,要加载某个??槭?,使用require进行加载,要暴露接口时,可以通过exports,module.exports和return

2、加载???/strong>

在前面定义??槭?,我们说过,当factory为函数时,require会作为默认参数传递进去,而require可以实现模块的加载

require是一个方法,接受??楸晔蹲魑ㄒ徊问美椿袢∑渌?樘峁┑慕涌?/p>

define(function(require, exports) {

  // 获取???a 的接口
  var a = require('./a');

  // 调用???a 的方法
  a.doSomething();

});

从上面定义模块和加载??榈姆绞缴希颐且部梢钥闯鯝MD和CMD主要有下面几个不同:

(1)AMD是RequireJS在推广过程中对??槎ㄒ宓墓娣痘?, CMD是SeaJS在推广过程中对模块定义的规范化产出
(2)对于依赖的???,AMD是提前执行,CMD是延迟执行
(3)对于依赖的???,AMD推崇依赖前置,CMD推崇依赖就近

3、seajs使用例子

因为CMD是SeaJS在推广过程中对??槎ㄒ宓墓娣痘?,因此一般在实际开发中,我们都是通过SeaJS进行模块的定义和加载

下面是一个简单的例子

// 定义??? myModule.js
define(function(require, exports, module) {
  var $ = require('jquery.js')
  $('div').addClass('active');
  exports.data = 1;
});

// 加载???seajs.use(['myModule.js'], function(my){
    var star= my.data;
    console.log(star);  //1
});

上面的代码中定义了myModule.js??椋蛭媚?橐览涤趈query.js,因此在需要使用该??槭笨梢允褂胷equire进行模块的加载,然后通过exports暴露出接口,通过SeaJS的use方法我们可以加载该??椋⑶沂褂酶媚?楸┞冻龅慕涌?/p>

四、es6中的模块化

在es6没有出来之前,社区制定了一些模块加载方案,最主要的有 CommonJS 和 AMD 两种,前者用于服务器,后者用于浏览器,ES6 在语言标准的层面上,实现了??楣δ?,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的??榻饩龇桨?/p>

es6中的??榛幸桓霰冉洗蟮奶氐?,就是实现尽量的静态化,比如说在CommonJS中我们要加载fs中的几个方法,需要这样写

// CommonJS???let { stat, exists, readFile } = require('fs');

// 等同于
let _fs = require('fs');
let stat = _fs.stat;
let exists = _fs.exists;
let readfile = _fs.readfile;

上面的代码其实是加载了fs中的所有方法,生成一个对象,再从这个对象上读取方法,这种加载其实叫做运行时加载,也就是只有运行时才能得到这个对象,不能实现在编译时实现静态优化

ES6 模块不是对象,而是通过export命令显式指定输出的代码,再通过import命令输入

// ES6模块
import { stat, exists, readFile } from 'fs';

上面代码的实质是从fs??榧釉?3 个方法,其他方法不加载,这种加载称为“编译时加载”或者静态加载,即 ES6 可以在编译时就完成??榧釉?,效率要比 CommonJS ??榈募釉胤绞礁?,当然,这也导致了没法引用 ES6 ??楸旧?,因为它不是对象

1、export

??楣δ苤饕闪礁雒罟钩桑篹xport和import,export命令用于规定??榈亩酝饨涌?,import命令用于输入其他??樘峁┑墓δ?/p>

一般来说,一个模块就是一个独立的文件,该文件内部的所有变量,外部无法获取,如果你希望外部能够读取??槟诓康哪掣霰淞浚捅匦胧褂胑xport关键字输出该变量

// profile.js
export var firstName = 'Michael';
export var lastName = 'Jackson';
export var year = 1958;

如果要输出函数,可以像下面这样定义

function v1() { ... }
function v2() { ... }

export {
  v1 as streamV1,
  v2 as streamV2,
  v2 as streamLatestVersion
};

上面的代码中,我们使用了as对函数的对外接口进行了重命名

2、import

使用export命令定义了??榈亩酝饨涌谝院螅渌?JS 文件就可以通过import命令加载这个???/p>

// main.js
import {firstName, lastName, year} from './profile.js';

function setName(element) {
  element.textContent = firstName + ' ' + lastName;
}

import命令接受一对大括号,里面指定要从其他??榈既氲谋淞棵?。大括号里面的变量名,必须与被导入??椋╬rofile.js)对外接口的名称相同

我们也可以对加载的模块进行重命名

import { lastName as surname } from './profile.js';

除了指定加载某个输出值,还可以使用整体加载,即用星号(*)指定一个对象,所有输出值都加载在这个对象上面

下面是一个circle.js文件,它输出两个方法area和circumference

// circle.js

export function area(radius) {
  return Math.PI * radius * radius;
}

export function circumference(radius) {
  return 2 * Math.PI * radius;
}

整体加载的写法如下

import * as circle from './circle';

console.log('圆面积:' + circle.area(4));
console.log('圆周长:' + circle.circumference(14));

这里有一个地方需要注意,??檎寮釉厮诘哪歉龆韵螅ㄉ侠?code>circle),应该是可以静态分析的,所以不允许运行时改变,下面的写法都是不允许的

import * as circle from './circle';

// 下面两行都是不允许的
circle.foo = 'hello';
circle.area = function () {};
最后编辑于
?著作权归作者所有,转载或内容合作请联系作者

  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,100评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,308评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事?!?“怎么了?”我有些...
    开封第一讲书人阅读 159,718评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,275评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,376评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,454评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,464评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,248评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,686评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,974评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,150评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,817评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,484评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,140评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,374评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,012评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,041评论 2 351

推荐阅读更多精彩内容