1. 什么是模块化?

将一个复杂的程序依据一定的规则封装成几个块(文件),并最终组合在一起。块(文件)的内部数据/实现是私有的,只是向外部暴露一些接口(方法)与外部其他模块通信。

2.模块的进化史

1.最早的代码风格,全局被污染,容易造成命名冲突。

function foo(){//...
}function bar(){//...
}

2.简单封装:namespace模式。虽然减少了全局变量的数目,但本质是对象,不安全。

var myApp={foo:function(){//...},bar:function(){//...}
}myApp.foo()

3.匿名闭包:IIFE模式,立即执行函数是局部作用域,不污染全局作用域。

var module=(function(){var _private="安全作用域";var foo=function(){console.log(_private)}return {foo}
})()module.foo();
module._private;//undefined

4.在立即执行函数中引入依赖:这也是现代模块实现的基石。

var module = (function ($) {var _$body = $('body')var foo = function () {console.log(_$body)}return {foo,}
})(jQuery)module.foo()

模块化的好处:

  • 避免命名冲突,减少命名空间污染
  • 更好的分离,按需加载
  • 更高的复用性
  • 更容易维护

缺点:

  • 由于一个文件被分割成多个文件,造成页面加载时需要更多的请求数,而往往我们需要减少发起请求的数量。
  • 依赖模糊,拆分的文件往往需要按照一定的顺序引入,这容易造成犯错。
  • 难以维护

3.CommonJS

说明:

  • 服务器端:模块的加载是运行时同步加载
  • 浏览器端:模块需要提前编译打包处理

基本语法

暴露模块:

  • module.exports=value
  • exports.xxx=value

引入模块:

  • require(xxx)

注意:

  • 当引入的模块是第三方模块,则xxx是模块名,比如(const jQuery = require('jQuery'))
  • 当引入的模块是自定义模块,则xxx是自定义模块的路径,比如(let myModule = require('./myModule.js'))

CommonJS常用于NodeJs中。

示例:

创建四个文件:module1.js、 module2.js、module3.js、app.js文件

module1.js

function foo() {console.log('module1  foo()')
}module.exports = {foo,
}

module2.js

module.exports = function () {console.log('module2()')
}

module3.js

exports.foo = function () {console.log('module3 foo()')
}exports.bar = function () {console.log('module3 bar()')
}

app.js用于引入模块

const module1 = require('./module1') //const { foo } = require('./module1');//使用了es6语法,进行对象解构
const module2 = require('./module2')
const module3 = require('./module3') //const {foo,bar} = require('./module3')module1.foo()
module2()
module3.foo()
module3.bar()

面试:module.exportsexports的区别?(重点)

找出区别需要了解的知识点:

1.在js模块中,有一个全局对象module,该module对象有个exports属性,exports属性初始默认值是一个空对象。即初始化时:module.exports = { }。
2.那exports是什么呢?在底层源码中,只是简单的exports = module.exports = { } ,即exports和module.exports是指向同一个堆地址。
3. 那么exports.foo = function() { xxx },实际上就是往module.exports指向的空对象中添加foo属性。
4.如果在一个模块中只是使用exports去暴露模块,那么module.exports与exports是相同的,没有什么区别,即exports是module.exports的一个引用。

特别注意:const xxx = require('./module.js') 中的xxx指的是什么呢?

无论模块中是使用exports.foo 暴露模块,还是使用module.exports去暴露模块,上面的xxx都只能是module.exports所指的内容,而不会是exports所指的内容。

思考问题

  • 问题一:为什么前面说一个模块中只是使用exports去暴露模块,exports与module.exports才会相同,难道使用module.exports去暴露模块,exports就不等于module.exports了吗?
  • 问题二:上面不是说exports与module.exports指向相同的地址吗?那么为什么还要关心require引入的xxx只能是module.exports。

答案:

在前面三个模块中,module1与module2两个模块,exports !== module.exports。而module3模块,exports === module.exports。

原因:(关键看module.exports有没有改变指针指向)

  • module1中:明显module.exports 使用了=号指向了一个新的对象,对象有个foo属性,而exports还是指向原本的堆地址,那么exports !== module.exports
 module.exports = {foo,}
  • module2中:同理,module.exports指向了一个函数,函数也是一个对象,也改变了地址
  • module3中:只是使用了exports.xxx,即module.exports没有改变指向,即exports === module.exports

4.ES6模块化

支撑ES6模块的两个主要新关键字是import和export。它们在语法上有许多微妙之处,所以我们来深入了解一下。

注意:

这里有一个很容易被忽略的重要细节:import和export都必须出现在使用它们的最顶层作用域。举例来说,不能把imort或export放在if条件中;它们必须出现在所有代码块和函数的外面。

4.1 导出export

export关键字或者是放在声明的前面,或者是作为一个操作符与一个要导出的绑定列表一起使用。

命名导出:

//export放在声明前面
export function foo() {//...
}
export var awesome = 42;//export 与要导出的绑定列表一起使用
var bar = [1,2,3];
export { bar }

下面是同样导出的另外一种表达形式:

function foo() {//...
}
var awesome = 42;
var bar = [1,2,3];
export { foo,awesome,bar } ;//导出绑定列表

命名导出时还可以“重命名”(即别名)一个模块的成员:

function foo(){}
export { foo as bar};

注意

  • 导入这个模块时,只有成员名称bar可以导入;foo还是隐藏在模块内部,即import { foo }是不可行的。
  • 命名导出可以取别名,在导入时也可以取别名,在导入时必须与你导出时的名称一一对应。

考虑(注意)

var awesome = 42;
export { awesome };awesome = 100

导入这个模块时,不管是在awesome = 100 之前还是之后,一旦赋值发生,导入的绑定就会决议到100而不是42.

默认导出:

每个模块只能有一个default,使用默认导出可以在导入模块时重命名。

关于默认导出有一个微妙的细节需要格外小心。比较下面两端代码:

function foo(){}
export default foo;
--------------------------------------
function foo(){}
export { foo as default }  //可以把它当成命名导出时取别名

微妙的细节:

  • 上面说了使用命名导出:导入这个模块时,不管是在awesome = 100 之前还是之后,一旦赋值发生,导入的绑定就会决议到100而不是42.即新值覆盖旧值.
  • 默认导出:如果之后你再给foo附一个不同的值,模块导入得到的仍然是原来导出的函数,而不是新的值。即新值不会覆盖旧值
  • 命名导出可以使用export var foo = … (或者 let 或者 const) ,但是默认导出只能export default function foo(){},而不能是export default var foo = … (或者 let 或者 const)

因为一个模块只能有一个default,所以你可能会忍不住把模块设计成默认导出一个对象

export default {foo,bar
}

但这是官方不建议采用的。如果你想导出多个你可以这样:

export default function foo(){}
export function bar(){}
export function baz(){}

即一个单独的默认导出,其它采用命名导出。

你也可以再次导出某个模块的导出,就像这样:

export {foo , bar} from "baz";
export {foo as FOO,bar as BAR } from "baz";
export * from "baz";

即先从“baz”模块导入,然后显示列出它的成员再从你的模块导出。但这些形式中,“baz”模块的成员不会导入到你的模块的局部作用域中,它们就像不留痕迹地穿过。

4.2 导入import

对于命名导出:

import {foo , bar , baz} from "foo";
或
import {foo as FOO,bar as BAR,baz as BAZ} from "foo"

注意:对于命名导出的模块,导入时需要用{ }包裹。

对于默认导出:

export default function foo(){}
import foo from "foo" || import xxx from "foo" xxx可以是任意名,即别名

当你的模块同时使用命名导出和默认导出时:

export default function foo(){}export function bar(){}
export function baz(){}导入这个模块的默认导出和它的两个命名导出:
import FOOFN, { bar , baz as BAZ } from “foo”;FOOFN(); //FOOFN是默认导出foo的别名
bar();
BAZ();

命名空间导入:

export function bar() {}
export var x =42;
export function baz() {}
export default function foo() {}//全部导入可以用命名空间导入方式,那么就不需要一个个全导入
import fooFun , * as hello from "foo"
fooFun();
hello.default()//fooFun === hello.default
hello.x;
hello.bar();
hello.baz();即import * as hello from "foo"也引入了默认导出,hello.default()调用即可。

5.AMD(Asynchronous Module Definition 异步模块定义)

说明:

  • 专门用于浏览器端,模块的加载是异步的。
  • require.js实现

基本语法

定义模块

  • 定义没有依赖的模块:
define(function(){return 模块
})
  • 定义有依赖的模块:
define(['module1','module2'],function(m1,m2){return 模块
})

引入模块

require(['module1','module2'],function(m1,m2){使用m1/m2模块
})

require.js使用教程

1.下载require.js, 并引入

  • 官网: http://www.requirejs.cn/
  • github : https://github.com/requirejs/requirejs
  • 将require.js导入项目: js/libs/require.js

2.创建项目结构

  |-js|-libs|-require.js|-modules|-alerter.js|-dataService.js|-main.js|-index.html

3.定义require.js的模块代码

  • dataService.js
   define(function () {let msg = 'atguigu.com'function getMsg() {return msg.toUpperCase()}return {getMsg}})
  • alerter.js
    define(['dataService', 'jquery'], function (dataService, $) {let name = 'Tom2'function showMsg() {$('body').css('background', 'gray')alert(dataService.getMsg() + ', ' + name)}return {showMsg}})

4.应用主(入口)js: main.js

  (function () {//配置requirejs.config({//基本路径baseUrl: "js/",//模块标识名与模块路径映射paths: {"alerter": "modules/alerter","dataService": "modules/dataService",}})//引入使用模块requirejs( ['alerter'], function(alerter) {alerter.showMsg()})})()

5.index.html

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>AMD示例</title></head><body><script data-main="js/main.js" src="js/libs/require.js"></script></body>
</html>

6.CMD (Common Module Definition 通用模块定义)–少用

说明:

  • 专门用于浏览器端,模块的加载是异步的。
  • sea.js实现
  • CMD是common.js与AMD的集合

基本语法

定义暴露模块:

  • 定义没有依赖的模块
define(function(require,exports,module){exports.xxx=valuemodule.exports=value
})
  • 定义有依赖的模块
define(function(require,exports,module){//同步引入依赖模块var module2 = require('./module2')//异步引入依赖模块require.async('./module3',function(m3){//...})//暴露模块exports.xxx=value
})

引入模块:

define(function(require){var m1 = require('./module1')var m3 = require('./module3')//使用m1/m3
})
sea.js简单使用教程
  1. 下载sea.js, 并引入
  • 官网: http://seajs.org/
  • github : https://github.com/seajs/seajs
  • 将sea.js导入项目: js/libs/sea.js
  1. 创建项目结构
  |-js|-libs|-sea.js|-modules|-module1.js|-module2.js|-module3.js|-module4.js|-main.js|-index.html
  1. 定义sea.js的模块代码
  • module1.js
define(function (require, exports, module) {//内部变量数据var data = 'atguigu.com'//内部函数function show() {console.log('module1 show() ' + data)}//向外暴露exports.show = show})
  • module2.js
 define(function (require, exports, module) {module.exports = {msg: 'I Will Back'}})
  • module3.js
  define(function (require, exports, module) {const API_KEY = 'abc123'exports.API_KEY = API_KEY})
  • module4.js
    define(function (require, exports, module) {//引入依赖模块(同步)var module2 = require('./module2')function show() {console.log('module4 show() ' + module2.msg)}exports.show = show//引入依赖模块(异步)require.async('./module3', function (m3) {console.log('异步引入依赖模块3  ' + m3.API_KEY)})})
  • main.js : 主(入口)模块
    define(function (require) {var m1 = require('./module1')var m4 = require('./module4')m1.show()m4.show()})
  1. index.html:
<script type="text/javascript" src="js/libs/sea.js"></script>
<script type="text/javascript">seajs.use('./js/modules/main')
</script>

JS模块化CommonJS、ES6模块化 、AMD、CMD知识总结相关推荐

  1. JS模块化(node.js模块化和es6模块化)

    JS 模块化 模块化概念 模块化是指解决一个复杂问题时,自顶向下逐层把系统划分为若干模块的过程,模块是可组合.分解和更换的单元. 模块化可提高代码的复用性和可维护性,实现按需加载. 模块化规范是对代码 ...

  2. 前端工程化(ES6模块化和webpack打包)

    文章目录 目标: 目录: 1.模块化相关规范 1.1.模块化概述 1.2.浏览器端模块化规范 AMD CMD 1.3.服务器端模块化规范 CommonJS 1.4.大一统的模块化规范-ES6模块化 1 ...

  3. ES6模块化与异步编程高级用法

    ES6模块化与异步编程高级用法 一.学习目标 能够知道如何使用ES6的模块化语法 能够知道如何使用Promise解决回调地域的问题 能够知道如何使用async/await 简化Promise的调用 能 ...

  4. 什么是ES6模块化?

    ES6中的模块化 1. 前端模块化规范的分类 在 ES6 模块化规范诞生之前,JavaScript 社区已经尝试并提出了AMD.CMD.CommonJS等模块化规范. 但是,这些由社区提出的模块化标准 ...

  5. 史上最详细易懂的ES6模块化语法(重点)

    对于初次学习ES6的小伙伴来说,ES6的模块化语法是一个重点,在没有模块化之前,前端js代码有一下三句话 1.私密不漏 2.重名不怕 3.依赖不乱 一.接下来先演示在没有模块化之前,这个"私 ...

  6. Requirejs与r.js打包,AMD、CMD、UMD、CommonJS、ES6模块化

    一:require.js 1.require.js是什么?为什么要用它? require.js是一个JavaScript模块载入框架,实现的是AMD规范.使用require.js有以下优点: ① 异步 ...

  7. JavaScript模块化 --- Commonjs、AMD、CMD、es6 modules

    随着前端js代码复杂度的提高,JavaScript模块化这个概念便被提出来,前端社区也不断地实现前端模块化,直到es6对其进行了规范,下面就介绍JavaScript模块化. 这篇文章还是希望能给大家一 ...

  8. JS JavaScript模块化(ES Module/CommonJS/AMD/CMD)

    前言 前端开发中,起初只要在script标签中嵌入几十上百行代码就能实现一些基本的交互效果,后来js得到重视,应用也广泛起来了, jQuery,Ajax,Node.Js,MVC,MVVM等的助力也使得 ...

  9. 浅谈js模块化:commons、AMD、CMD、ES6几种模块化的用法及各自的特点

    文章目录 一个页面需要引入多个js文件引发的问题: 模块化的好处: 几种常用的模块化规范 1. commonJs 2. AMD 3. ES6 4.CMD 总结 js模块化是现在比较流行的一种用法,它能 ...

  10. 前端模块化CommonJS、AMD、CMD、ES6

    一.前端模块化 什么是模块化?为什么前端需要模块化? js代码量激增,放在同一个文件里面,不容易维护,而且牵一发而动全身. 这时候就需要将代码按照逻辑放在不同的文件里面,按照一定的语法规则,遵循特定的 ...

最新文章

  1. Android中设置控件可见与不可见详解
  2. autoware源码安装与运行ROSBAG示例(二)
  3. 软件开发流程包含哪些内容
  4. poj3621 Sightseeing Cows --- 01分数规划
  5. @RequestBody注解失效?从前端传来的数据到底需不需要@RequestBody注解?前端传输数据解析的问题?
  6. 11.Windows线程切换_线程优先级
  7. Markdown:数学公式练习(3)
  8. Win2003 + SQL 2005 做数据库集群总结(虚拟机)
  9. ConcurrentHashMap--自用,非教学
  10. Top 10 Project Management Software
  11. OAF_OAF组件系列1 - Item Style汇总(概念)
  12. 扩展RocketMQ 使其支持任意时间精度的消息延迟
  13. LAMP架构mariadb/apache的安装及基本使用
  14. oracle8ir2,Oracle8iR2(oracle8.1.6)在redhat linux 7.1上的简易安装方法
  15. C语言程序设计期末卷子,C语言程序设计试题1
  16. asp.net中涉及子文件夹的母版页和相对路径文件引用问题(一)
  17. 直播app源码,隐藏导航栏和状态栏
  18. 计算机函数sumif怎么用,怎么用sumif函数求和
  19. QEMU虚拟机源码分析概论
  20. 2020 用html jQuery实现广告轮播图自动切换 滚动页面 鼠标悬浮下标且左右切换图片

热门文章

  1. 学生信息管理系统 jsp + servlet + mysql (2020)
  2. python卡方检验计算pvalue值_Python数据科学:卡方检验
  3. 小智机器人江苏赞_小智伴机器人外观说明
  4. 《非理性繁荣》读书笔记优秀作文5000字
  5. 如何判断时间复杂度和空间复杂度
  6. Qt入门教程【硬件编程】串口基础知识
  7. 论文学习笔记 How not to prove your election outcome
  8. 麻烦不断的国外科技公司排行榜:苹果惨上榜
  9. pdo操作oracle,PDO基础操作
  10. ptp输出内容包含什么_PTP 无线传输