一、什么是模块化?

到底什么是模块化、模块化开发呢?

  • 事实上模块化开发最终的目的是将程序划分成一个个小的结构;
  • 这个结构中编写属于自己的逻辑代码,有自己的作用域,不会影响到其他的结构;
  • 这个结构可以将自己希望暴露的变量、函数、对象等导出给其结构使用;
  • 也可以通过某种方式,导入另外结构中的变量、函数、对象等;

上面说提到的结构,就是模块;按照这种结构划分开发程序的过程,就是模块化开发的过程;

二、没有模块化带来的问题

早期没有模块化带来了很多的问题:比如命名冲突的问题

当然,我们有办法可以解决上面的问题:立即函数调用表达式(IIFE)

  • IIFE (Immediately Invoked Function Expression)

但是,我们其实带来了新的问题:

  • 第一,我必须记得每一个模块中返回对象的命名,才能在其他模块使用过程中正确的使用;
  • 第二,代码写起来混乱不堪,每个文件中的代码都需要包裹在一个匿名函数中来编写
  • 第三,在没有合适的规范情况下,每个人、每个公司都可能会任意命名、甚至出现模块名称相同的情况;

所以,我们会发现,虽然实现了模块化,但是我们的实现过于简单,并且是没有规范的。

  • 我们需要制定一定的规范来约束每个人都按照这个规范去编写模块化的代码;
  • 这个规范中应该包括核心功能:模块本身可以导出暴露的属性,模块又可以导入自己需要的属性;
  • JavaScript社区为了解决上面的问题,涌现出一系列好用的规范,接下来我们就学习具有代表性的一些规范。

三、CommonJS规范和Node关系

CommonJS是一个规范,最初提出来是在浏览器以外的地方使用,并且当时被命名为ServerJS,后来为了体现它的广泛性,修改为CommonJS,平时我们也会简称为CJS。

  • Node是CommonJS在服务器端一个具有代表性的实现;
  • Browserify是CommonJS在浏览器中的一种实现;
  • webpack打包工具具备对CommonJS的支持和转换;

所以,Node中对CommonJS进行了支持和实现,让我们在开发node的过程中可以方便的进行模块化开发:

  • 在Node中每一个js文件都是一个单独的模块;
  • 这个模块中包括CommonJS规范的核心变量:exports、module.exports、require
  • 我们可以使用这些变量来方便的进行模块化开发;

前面我们提到过模块化的核心是导出和导入,Node中对其进行了实现:

  • exports和module.exports可以负责对模块中的内容进行导出
  • require函数可以帮助我们导入其他模块(自定义模块、系统模块、第三方库模块)中的内容

四、exports导出

注意:exports是一个对象,我们可以在这个对象中添加很多个属性,添加的属性会导出;

上面的代码意味着:

  • main中的why变量等于exports对象
  • 也就是require通过各种查找方式,最终找到了exports这个对象;
  • 并且将这个exports对象赋值给了why变量;
  • why变量就是exports对象了;

五、module.exports

但是Node中我们经常导出东西的时候,又是通过module.exports导出的,module.exports和exports有什么关系或者区别呢?

我们追根溯源,通过维基百科中对CommonJS规范的解析:

  • CommonJS中是没有module.exports的概念的;
  • 但是为了实现模块的导出,Node中使用的是Module的类,每一个模块都是Module的一个实例,也就是module;
  • 所以在Node中真正用于导出的其实根本不是exports,而是module.exports
  • 因为module才是导出的真正实现者;

但是,为什么exports也可以导出呢?

  • 这是因为module对象的exports属性是exports对象的一个引用
module.exports = {};
exports = module.exports;
  • 也就是说 module.exports = exports = main中的why;

六、require细节

我们现在已经知道,require是一个函数,可以帮助我们引入一个文件(模块)中导出的对象。那么,require的查找规则是怎么样的呢?

https://nodejs.org/dist/latest-v14.x/docs/api/modules.html#modules_all_together

这里总结比较常见的查找规则:导入格式如下:require(X)



七、模块的加载过程

结论一:模块在被第一次引入时,模块中的js代码会被运行一次

结论二:模块被多次引入时,会缓存,最终只加载(运行)一次

  • 为什么只会加载运行一次呢?
  • 这是因为每个模块对象module都有一个属性:loaded。
  • 为false表示还没有加载,为true表示已经加载;

结论三:如果有循环引入,那么加载顺序是什么?
如果出现下图模块的引用关系,那么加载顺序是什么呢?

  • 这个其实是一种数据结构:图结构;
  • 图结构在遍历的过程中,有深度优先搜索(DFS, depth first search)和广度优先搜索(BFS, breadth first search);
  • Node采用的是深度优先算法:main -> aaa -> ccc -> ddd -> eee ->bbb

八、CommonJS规范缺点

CommonJS加载模块是同步的:

  • 同步的意味着只有等到对应的模块加载完毕,当前模块中的内容才能被运行;
  • 这个在服务器不会有什么问题,因为服务器加载的js文件都是本地文件,加载速度非常快;

如果将它应用于浏览器呢?

  • 浏览器加载js文件需要先从服务器将文件下载下来,之后再加载运行;
  • 那么采用同步的就意味着后续的js代码都无法正常运行,即使是一些简单的DOM操作;

所以在浏览器中,我们通常不使用CommonJS规范

  • 当然在webpack中使用CommonJS是另外一回事;
  • 因为它会将我们的代码转成浏览器可以直接执行的代码;

在早期为了可以在浏览器中使用模块化,通常会采用AMD或CMD:

  • 但是目前一方面现代的浏览器已经支持ES Modules,另一方面借助于webpack等工具可以实现对CommonJS或者ES Module代码的转换;
  • AMD和CMD已经使用非常少了,所以这里我们进行简单的演练;

AMD主要是应用于浏览器的一种模块化规范:

  • AMD是Asynchronous Module Definition(异步模块定义)的缩写;
  • 它采用的是异步加载模块
  • 事实上AMD的规范还要早于CommonJS,但是CommonJS目前依然在被使用,而AMD使用的较少了;

规范只是定义代码的应该如何去编写,只有有了具体的实现才能被应用:

  • AMD实现的比较常用的库是require.js和curl.js;

CMD规范也是应用于浏览器的一种模块化规范:

  • CMD 是Common Module Definition(通用模块定义)的缩写;
  • 它也采用了异步加载模块,但是它将CommonJS的优点吸收了过来;
  • 但是目前CMD使用也非常少了;

CMD也有自己比较优秀的实现方案: SeaJS

九、认识 ES Module

ES Module和CommonJS的模块化有一些不同之处:

  • 一方面它使用了import和export关键字;
  • 另一方面它采用编译期的静态分析,并且也加入了动态引用的方式

ES Module模块采用export和import关键字来实现模块化:

  • export负责将模块内的内容导出;
  • import负责从其他模块导入内容;

了解:采用ES Module将自动采用严格模式:use strict

十、exports关键字

export关键字将一个模块中的变量、函数、类等导出;

我们希望将其他中内容全部导出,它可以有如下的方式:

  • 方式一:在语句声明的前面直接加上export关键字
  • 方式二:将所有需要导出的标识符,放到export后面的 {}中
    注意:这里的 {}里面不是ES6的对象字面量的增强写法,{}也不是表示一个对象的;
    所以: export {name: name},是错误的写法;

  • 方式三:导出时给标识符起一个别名

十一、import关键字

import关键字负责从另外一个模块中导入内容

导入内容的方式也有多种:

方式一:import {标识符列表} from ‘模块’;

  • 注意:这里的{}也不是一个对象,里面只是存放导入的标识符列表内容;

方式二:导入时给标识符起别名

方式三:通过 * 将模块功能放到一个模块功能对象(a module object)上

十二、export和import结合使用

补充:export和import可以结合使用

为什么要这样做呢?

  • 在开发和封装一个功能库时,通常我们希望将暴露的所有接口放到一个文件中;
  • 这样方便指定统一的接口规范,也方便阅读;
  • 这个时候,我们就可以使用export和import结合使用;

十三、default用法

前面我们学习的导出功能都是有名字的导出(named exports):

  • 在导出export时指定了名字;
  • 在导入import时需要知道具体的名字;

还有一种导出叫做默认导出(default export)

  • 默认导出export时可以不需要指定名字;
  • 在导入时不需要使用 {},并且可以自己来指定名字;
  • 它也方便我们和现有的CommonJS等规范相互操作;

注意:在一个模块中,只能有一个默认导出(default export);

十四、import函数

通过import加载一个模块,是不可以在其放到逻辑代码中的,比如:

为什么会出现这个情况呢?

  • 这是因为ES Module在被JS引擎解析时,就必须知道它的依赖关系;
  • 由于这个时候js代码没有任何的运行,所以无法在进行类似于if判断中根据代码的执行情况;
  • 甚至下面的这种写法也是错误的:因为我们必须到运行时能确定path的值;

但是某些情况下,我们确确实实希望动态的来加载某一个模块:

  • 如果根据不同的条件,动态来选择加载模块的路径;
  • 这个时候我们需要使用 import() 函数来动态加载;

    import.meta是一个给JavaScript模块暴露特定上下文的元数据属性的对象。
  • 它包含了这个模块的信息,比如说这个模块的URL;
  • 在ES11(ES2020)中新增的特性;

JS高级——模块化学习笔记相关推荐

  1. JavaScript高级程序设计学习笔记(三)

    分享一下第五章(引用类型)的笔记,内容比较多,我拆成了两部分,今天这部分是关于Object.Array.Date和RegExp类型的. 以下的笔记是书上一些我以前学习的时候,没有太重视的js基础知识, ...

  2. JS高级的学习(二)

    JS高级的学习(二) set对象 Set 是一个对象 存放数据 数据永远不会重复 Set 当成是一个数组 遍历 使用 数组方法 find findIndex Map 数组转成 Set对象 const ...

  3. 【C#8.0 and .NET Core 3.0 高级编程学习笔记】

    @C#8.0 and .NET Core 3.0 高级编程学习笔记 前言 为了能精细地完成对C#语言的学习,我决定选择一本书,精读它,理解它,记录它.我想选择什么书并不是最重要的,最重要的是持之以恒的 ...

  4. JS高级程序设计——阅读笔记四

    JS高级程序设计--阅读笔记四 第六章 集合引用类型 6.1 Object 6.2 Array 6.2.1 创建数组 from()函数 6.2.2 数组空位 6.2.3 数组索引 6.2.4 检测数组 ...

  5. vue 切换页面没有改变滚动条_Web前端高级Vue学习笔记(三)

    大家好,我是轻风乍起,本人从事前端8年了,总结了很多学习资料,有时间我会普及一些入门和提升建议,思想提升了,路线明确了自然就好了,于是就整理了一些学习笔记, 供大家学习,干货福利内容 在文末↓ 名称1 ...

  6. 【前端】-【node.js基础】-学习笔记

    [前端]-[node.js]-学习笔记 1 node.js介绍 1.1 node.js优点 1.2 node.js 不足之处 1.3 nodejs与java的区别 2. node中函数 3. 浏览器和 ...

  7. ASP.NET MVC5 高级教程 学习笔记

    // 本应用是ASP.NET MVC5 高级教程学习产物 // 2015-07-23 // 第一章 入门 // 第二章 控制器 // 第三章 视图 1.Install-Package Wrox.Pro ...

  8. lunar.js 基本使用学习笔记

    文章目录 lunar 基本使用学习笔记 介绍 基本使用 部分文档 API 转载 阳历相关的方法 API Solar 阳历 阳历实例化 阳历对象可以使用多种字符串输出方式: 获取年.月.日 儒略日 获取 ...

  9. 高级编程学习笔记day01(知识点篇)

    文件IO学习笔记 1. 文件描述符:所有打开的文件都通过文件描述符引用.     文件描述符0与进程的标准输入关联     文件描述符1与进程的标准输出关联     文件描述符2与进程的标准错误关联 ...

最新文章

  1. 如何使用TVM Pass Relay
  2. 企业官网页面设计谨记三个要点!
  3. keil5图标变成白色_【网上最简单】Chrome安装后打不开任何页面 amp; 改名后图标变成小白块[30秒解决]...
  4. [幽默漫画]对于程序猿来说deadline很容易搞定!
  5. Android 多媒体开发学习之创建图片的副本
  6. redis 主从不同步连接不上
  7. postsql 10.4安装失败
  8. java 换行规范_JAVA代码规范(一)
  9. 数学建模之数据包络分析(评价投入产出比的模型)
  10. python自动化webdriver_轻松自动化---selenium-webdriver(python) (六)
  11. 听说你的淘客群又做死了?来学学群维护这几招!
  12. HadoopYarn设置Fair Scheduler公平调度器
  13. 男子打着点滴参观博物馆 被称“挂水哥”感动南博(图)
  14. python 相关性分析原理及代码详细介绍
  15. 客户成功 | 数据解码技能提升,Smartbi助力长沙烟草找到“新路子”
  16. 刚才玩了下Steganos Internet Anonym,IP伪装器,每一秒自动换一IP
  17. 【转】PIC单片机的配置字总结
  18. java代码审计手书(三)
  19. Windows 2003 SP2 截至 8.14 更新补丁汇总
  20. MySQL索引机制-图灵教育诸葛老师

热门文章

  1. 验证软件需求正确性的四个角度
  2. 学成在线--23.课程图片管理(上传图片)
  3. Linux 下的/usr/bin /usr/sbin /usr/local/bin /usr/local/sbin区别
  4. python中continue只结束本次循环_循环(while,break,continue),转义字符
  5. Django后台项目之用户管理功能开发流程
  6. HTML段落,换行,字符实体
  7. python中的流程控制
  8. 【MySQL】Linux端-实现Mysql数据定时自动备份
  9. 【机器学习】朴素贝叶斯介绍及实例--对短信进行二分类 使用多项式分布
  10. Flask唯一URL规则--@app.route('/', endpoint='1')