JavaScript模块化编程规范之CommonJS、AMD、CMD、ES6

本篇文章给大家带来了关于javascript中的相关知识,其中主要介绍了模块化编程规范,commonjs、amd、cmd以及es6的相关问题,希望对大家有帮助。

JavaScript模块化编程规范之CommonJS、AMD、CMD、ES6

相关推荐:javascript学习教程

一、前言

AMD、CMD、CommonJs是ES5中提供的模块化编程方案,import/export是ES6中新增的模块化编程方案。

那么,究竟什么什么是AMD、CMD、CommonJs?他们之间又存在什么区别呢?项目开发应该选用哪种模块化编程规范,又是如何使用?本篇博文将一一解答以上疑问。

立即学习“Java免费学习笔记(深入)”;

二、AMD-异步模块定义

AMD是”Asynchronous Module Definition”的缩写,即”异步模块定义”。它采用异步方式加载模块,模块的加载不影响它后面语句的运行。

这里异步指的是不堵塞浏览器其他任务(dom构建,css渲染等),而加载内部是同步的(加载完模块后立即执行回调)。

RequireJS:是一个AMD框架,可以异步加载JS文件,按照模块加载方法,通过define()函数定义,第一个参数是一个数组,里面定义一些需要依赖的包,第二个参数是一个回调函数,通过变量来引用模块里面的方法,最后通过return来输出。

AMD是RequireJS在推广过程中对模块定义的规范化产出,它是一个概念,RequireJS是对这个概念的实现,就好比JavaScript语言是对ECMAScript规范的实现。AMD是一个组织,RequireJS是在这个组织下自定义的一套脚本语言。

不同于CommonJS,它要求两个参数:

require([module], callback);

登录后复制

第一个参数[module],是一个数组,里面的成员是要加载的模块,callback是加载完成后的回调函数。如果将上述的代码改成AMD方式:

require(['math'], function(math) {  math.add(2, 3);})

登录后复制

其中,回调函数中参数对应数组中的成员(模块)。

requireJS加载模块,采用的是AMD规范。也就是说,模块必须按照AMD规定的方式来写。

具体来说,就是模块书写必须使用特定的define()函数来定义。如果一个模块不依赖其他模块,那么可以直接写在define()函数之中。

define(id, dependencies, factory);

登录后复制

id:模块的名字,如果没有提供该参数,模块的名字应该默认为模块加载器请求的指定脚本名字;dependencies:模块的依赖,已被模块定义的模块标识的数组字面量。依赖参数是可选的,如果忽略此参数,它应该默认为 [“require”, “exports”, “module”]。然而,如果工厂方法的长度属性小于3,加载器会选择以函数的长度属性指定的参数个数调用工厂方法。factory:模块的工厂函数,模块初始化要执行的函数或对象。如果为函数,它应该只被执行一次。如果是对象,此对象应该为模块的输出值。

假定现在有一个math.js文件,定义了一个math模块。那么,math.js书写方式如下:

// math.jsdefine(function() {  var add = function(x, y) {    return x + y;  }  return  {    add: add  }})

登录后复制

加载方法如下:

// main.jsrequire(['math'], function(math) {  alert(math.add(1, 1));})

登录后复制

如果math模块还依赖其他模块,写法如下:

// math.jsdefine(['dependenceModule'], function(dependenceModule) {    // ...})

登录后复制

当require()函数加载math模块的时候,就会先加载dependenceModule模块。当有多个依赖时,就将所有的依赖都写在define()函数第一个参数数组中,所以说AMD是依赖前置的。这不同于CMD规范,它是依赖就近的。
CMD

三、CMD-同步模块定义

CMD 即Common Module Definition通用模块定义,是SeaJS在推广过程中对模块定义的规范化产出,是一个同步模块定义,是SeaJS的一个标准,SeaJS是CMD概念的一个实现,SeaJS是淘宝团队玉伯提供的一个模块开发的js框架。CMD规范是国内发展出来的,就像AMD有个requireJS,CMD有个浏览器的实现SeaJS,SeaJS要解决的问题和requireJS一样,只不过在模块定义方式和模块加载(可以说运行、解析)时机上有所不同。

CMD 通过define()定义,没有依赖前置,通过require加载jQuery插件,CMD是依赖就近,在什么地方使用到插件就在什么地方require该插件,即用即返,这是一个同步的概念。

在 CMD 规范中,一个模块就是一个文件。代码的书写格式如下:

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

登录后复制

其中,

require是可以把其他模块导入进来的一个参数;exports是可以把模块内的一些属性和方法导出的;module 是一个对象,上面存储了与当前模块相关联的一些属性和方法。

AMD是依赖关系前置,在定义模块的时候就要声明其依赖的模块;
CMD是按需加载依赖就近,只有在用到某个模块的时候再去require,示例代码如下:

// CMDdefine(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()  ...})

登录后复制

四、CommonJS 规范

CommonJS规范是通过module.exports定义的,在前端浏览器里面并不支持module.exports,通过node.js后端使用。Nodejs端使用CommonJS规范,前端浏览器一般使用AMD、CMD、ES6等定义模块化开发规范。

CommonJS的终极目标是提供一个类似Python,Ruby和Java的标准库。这样的话,开发者可以使用CommonJS API编写应用程序,然后这些应用就可以运行在不同的JavaScript解释器和不同的主机环境中。

在兼容CommonJS的系统中,你可以使用JavaScript开发以下程序:

服务器端JavaScript应用程序;命令行工具;图形界面应用程序;混合应用程序(如,Titanium或Adobe AIR);

2009年,美国程序员Ryan Dahl创造了node.js项目,将javascript语言用于服务器端编程。这标志”Javascript模块化编程”正式诞生。NodeJS是CommonJS规范的实现,webpack 也是以CommonJS的形式来书写。

node.js的模块系统,就是参照CommonJS规范实现的。在CommonJS中,有一个全局性方法require(),用于加载模块。假定有一个数学模块math.js,就可以像下面这样加载。

var math = require('math');

登录后复制

然后,就可以调用模块提供的方法:

var math = require('math');math.add(2,3); // 5

登录后复制

CommonJS定义的模块分为:模块引用(require)模块定义(exports)模块标识(module)
其中,

require()用来引入外部模块;exports对象用于导出当前模块的方法或变量,唯一的导出口;module对象就代表模块本身。

虽说NodeJS遵循CommonJS的规范,但是相比也是做了一些取舍,添了一些新东西的。

NPM作为Node包管理器,同样遵循CommonJS规范。

下面讲讲commonJS的原理以及简易实现:

1、原理
浏览器不兼容CommonJS的根本原因,在于缺少四个Node.js环境变量。

moduleexportsrequireglobal

登录后复制

只要能够提供这四个变量,浏览器就能加载 CommonJS 模块。

下面是一个简单的示例。

var module = {  exports: {}};(function(module, exports) {  exports.multiply = function (n) { return n * 1000 };  }(module, module.exports))var f = module.exports.multiply;  f(5) // 5000

登录后复制

上面代码向一个立即执行函数提供 module 和 exports 两个外部变量,模块就放在这个立即执行函数里面。模块的输出值放在 module.exports 之中,这样就实现了模块的加载。

2、Browserify 的实现
Browserify 是目前最常用的 CommonJS 格式转换工具。

请看一个例子,main.js 模块加载 foo.js 模块。

// foo.jsmodule.exports = function(x) {  console.log(x);};// main.jsvar foo = require("./foo");foo("Hi");

登录后复制

使用下面的命令,就能将main.js转为浏览器可用的格式。

$ browserify main.js > compiled.js

登录后复制

其中,Browserify到底做了什么?安装一下browser-unpack,就清楚了。

$ npm install browser-unpack -g

登录后复制

然后,将前面生成的compile.js解包。

$ browser-unpack < compiled.js

登录后复制

[  {    "id":1,    "source":"module.exports = function(x) {  console.log(x);};",    "deps":{}  },  {    "id":2,    "source":"var foo = require("./foo");foo("Hi");",    "deps":{"./foo":1},    "entry":true  }]

登录后复制

可以看到,browerify 将所有模块放入一个数组,id 属性是模块的编号,source 属性是模块的源码,deps 属性是模块的依赖。

因为 main.js 里面加载了 foo.js,所以 deps 属性就指定 ./foo 对应1号模块。执行的时候,浏览器遇到 require(‘./foo’) 语句,就自动执行1号模块的 source 属性,并将执行后的 module.exports 属性值输出。

五、ES6

有关es6模块特性,强烈推荐阮一峰老师的:ECMAScript 6 入门 – Module 的语法专栏。

要说 ES6 模块特性,那么就先说说 ES6 模块跟 CommonJS 模块的不同之处。

ES6 模块输出的是值的引用,输出接口动态绑定,而 CommonJS 输出的是值的拷贝;ES6 模块编译时执行,而 CommonJS 模块总是在运行时加载。

CommonJS 模块输出的是值的拷贝(原始值的拷贝),也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。

// a.jsvar b = require('./b');console.log(b.foo);setTimeout(() => {  console.log(b.foo);  console.log(require('./b').foo);}, 1000);// b.jslet foo = 1;setTimeout(() => {  foo = 2;}, 500);module.exports = {  foo: foo,};// 执行:node a.js// 执行结果:// 1// 1// 1

登录后复制

上面代码说明,b 模块加载以后,它的内部 foo 变化就影响不到输出的 exports.foo 了。这是因为 foo 是一个原始类型的值,会被缓存。所以如果你想要在 CommonJS 中动态获取模块中的值,那么就需要借助于函数延时执行的特性。

// a.jsvar b = require('./b');console.log(b.foo);setTimeout(() => {  console.log(b.foo);  console.log(require('./b').foo);}, 1000);// b.jsmodule.exports.foo = 1;   // 同 exports.foo = 1 setTimeout(() => {  module.exports.foo = 2;}, 500);// 执行:node a.js// 执行结果:// 1// 2// 2

登录后复制

所以我们可以总结一下:

CommonJS 模块重复引入的模块并不会重复执行,再次获取模块直接获得暴露的module.exports 对象。如果你需要处处获取到模块内的最新值的话,也可以每次更新数据的时候每次都要去更新 module.exports 上的值如果暴露的 module.exports 的属性是个对象,那就不存在这个问题了。

相关推荐:javascript视频教程

以上就是JavaScript模块化编程规范之CommonJS、AMD、CMD、ES6的详细内容,更多请关注【创想鸟】其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至253000106@qq.com举报,一经查实,本站将立刻删除。

发布者:PHP中文网,转转请注明出处:https://www.chuangxiangniao.com/p/2702449.html

(0)
上一篇 2025年3月7日 19:24:15
下一篇 2025年3月7日 19:24:21

AD推荐 黄金广告位招租... 更多推荐

相关推荐

  • JavaScript中栈和堆的区别是什么

    区别:1、在内存操作中,栈由操作系统自动分配和释放,而堆由开发人员自主分配和释放;2、在数据结构中,栈是一种运算受限的线性表,只允许表的一端进行插入和删除操作,而堆是一种优先队列,会根据优先级找优先度最高的先执行。 本教程操作环境:wind…

    2025年3月7日
    000
  • javascript技巧之拆箱装箱和类型转换

    本篇文章给大家带来了关于javascript中的相关知识,其中主要介绍了拆箱装箱和类型转换的相关问题,装箱是指把基本数据类型转换为对应的引用类型的操作,下面就一起来看一下,希望对大家有帮助。 相关推荐:javascript教程 基本数据类型…

    2025年3月7日
    200
  • 详细介绍JavaScript怎么实现哈希表

    本篇文章给大家带来了关于javascript中的相关知识,其中主要介绍了关于javascript怎么实现哈希表的相关问题,对最终数据插入的数组进行整个结构的封装,得到的就是哈希表,希望对大家有帮助。 相关推荐:javascript学习教程 …

    2025年3月7日 编程技术
    200
  • 一起来聊聊JavaScript函数柯里化

    本篇文章给大家带来了关于javascript中的相关知识,其中主要介绍了javascript中函数柯里化的相关问题,柯里化是把接受多个参数的函数变换成接受一个单一参数的函数,并且返回接受余下的参数且返回结果的新函数的技术,希望对大家有帮助。…

    2025年3月7日
    200
  • es6中怎么将set转化为数组

    es6中将set转化为数组的方法:1、使用扩展运算符“…”,语法“[…set对象]”;2、使用Array.from()方法,语法“Array.from(set对象)”。 本教程操作环境:windows7系统、ECMA…

    2025年3月7日
    200
  • es6怎么判断数组是否含有相同的值

    判断方法:1、将数组转为Set集合,并使用size属性获取Set元素总数,语法“new Set(arr).size”;2、利用length属性获取数组元素总数;3、比较Set元素总数和数组元素总数是否相等,不相等则含有相同的值,反之则没有。…

    2025年3月7日
    200
  • 实例图文详解在JavaScript中实现队列

    本篇文章给大家带来了关于javascript的相关知识,其中主要介绍了javascript实现队列的相关问题,描述队列数据结构,其具有的操作以及展示javascript中的队列实现,希望对大家有帮助。 相关推荐:javascript教程 1…

    2025年3月7日 编程技术
    200
  • 怎么快速掌握正则表达式?通过 AST 来学学正则语法!

    正则表达式是对字符串操作的一种逻辑公式,是在处理文本数据时的一项重要而复杂的技术。那么怎么快速掌握正则表达式?下面本篇文章推荐一种学习方法:通过 ast。希望对大家有所帮助! 字符串的处理基本都会用正则表达式,用它来做字符串的匹配、提取、替…

    2025年3月7日 编程技术
    200
  • javascript定义函数的几种类型是什么

    在JavaScript中定义函数的几种类型:1、利用function关键字通过声明或表达式定义函数,语法为“function functionName(parameters){执行的代码}”;2、利用函数构造器Function()构造函数。…

    2025年3月7日
    200
  • 带你了解JSON.stringify,看看怎么使用

    你真的会使用强大的json.stringify方法吗?下面本篇文章带大家详细了解解json.stringify,介绍一下使用方法,希望对大家有所帮助! JSON.stringify 作为日常开发中经常使用的方法,你真的能灵活运用它吗? 学习…

    2025年3月7日
    200

发表回复

登录后才能评论