JavaScript模块化开发的前世今生

一个兜兜转转,从“北深”回到三线城市的小码农,热爱生活,热爱技术,在这里和大家分享一个技术人员的点点滴滴。欢迎大家关注我的微信公众号:果冻想

前言

现代化的编程语言,基本都支持模块化的开发,咱不说别的,就最原始的Shell,我们公司都整了一套模块化开发的框架,进行模块化开发。但是,日常在编写JavaScript代码,或者阅读别人的JavaScript代码时,总是看到requireimportexport等等关键字,都说是JavaScript中的模块化的开发方式,这直接就把我整懵逼了,这怎么一个模块化的开发就搞出这么多的东西啊,这么多的关键词啊,入门即让人放弃?

无论是我这样的新手,还是一些老手,都对这个JavaScript中的模块化开发懵懵懂懂的,我就是这个样子的。那这里就通过一篇文章来把JavaScript模块化开发的前世今生给讲透了,让大家以后不再对这个知识点感到迷茫。

为啥需要模块化?

这就好比,动物园里一堆动物,是放在一个大院子里好管理呢,还是说每种动物用单间进行好管理。

比如,我们网页中引入JavaScript代码,经常是这样的。

<script src="./a.js"></script>  
<script src="./b.js"></script>  
<script src="./c.js"></script>

每个JS代码内容如下:

// a.js
var a = 1;setTimeout(() => console.log(a), 2000);// b.js
var a = 2;// c.js
var a = 3;

执行后,输出a = 3。虽然每个代码块处在不同的文件中,但是最终所有JS变量还是会处在同一个全局作用域下,这时候就需要额外注意由于作用域变量提升所带来的问题。

这就是说,虽然三段代码写在不同的文件中,但是因为运行时声明变量都在全局下,最终会产生冲突。同时,如果代码块之间有依赖关系的话,这将是一个非常棘手的问题,谁先加载,谁后加载,直接影响程序的运行。这么不智能的东西,在现代化的编程领域,是绝对不允许存在的。所以大佬们想的:

  1. 需要实现每个模块都要有自己单独的作用域,每个模块之间的内部变量不会产生冲突;
  2. 需要实现每个模块之间能通过某种规范或机制实现通信;
  3. 需要实现每个模块需要能导入其它模块的功能,也能导出自己的功能供其它模块调用。

好了,大佬们都有想法,纷争的时刻到了。

CommonJS规范

首先出厂的是CommonJS规范。其实我一开始是不知道这货的,只是在学习Node.js的时候,发现这货的。后来了解了下Node.js和CommonJS的关系。

即使你不会Node.js,你也会知道,Node.js这么大的一个生态体系,那肯定是众人拾柴火焰高的,肯定是不同的大神贡献了不同的包和模块的,那这些包和模块给我们,我们如何把他们组装在一起,而且还不出问题呢,这么头疼的问题,你想到了,我想到了,那Node.js的大佬肯定也想到了。

但是Node.Js刚出来的时候,没有官方的模块化规范,所以它就选择使用社区提供的CommonJS作为模块化规范。这下你就明白了这里面的缘由了。

作为一个模块化的规范,那肯定就要有导入和导出功能了。现在来看看CommonJS具体的规范内容:

  • 使用exports导出模块,使用require导入模块;
  • 如果JS文件中存在exports或者require关键字,那这个JS文件就是一个模块;
  • 模块内的全部代码均为模块内部代码,包括全局变量、全局函数,这些全局的内容均不会对全局变量造成污染。

话不多说,上代码,使用CommonJS实现一个小模块,导出一些功能,并在index.js中导入这些功能。

// 这里定义一个模块
// 定义两个变量
var age = 0;
var name = "果冻想";// 定义一个函数
function getAge() {return age;
}// 向外暴漏
exports.getAge = getAge;
exports.name = name;

在main.js中进行模块引入:

// console.log(age); 抛出异常,age未定义,未导出的情况下,只能在模块内部可见
// console.log(name); 抛出异常,name未定义,导出的情况下,需要通过模块进行访问// 引入模块
var module = require("./module.js");console.log(module.getAge()); // 输出0
console.log(module.name); // 输出果冻想

通过上面的代码,所有在模块内部的变量或函数,如果没有导出,外部就都无法访问,这样就解决了全局变量被污染的问题了。同时,我们也发现了,CommonJS主要是在Node.js中使用,但是在Node.js中,为了让我们使用CommonJS时更舒服,隐藏了很多实现细节,下面我们来看看这些实现细节。

  • 为了实现模块化,Node.js在引入模块时,它会将模块代码放到一个自执行函数中执行,以实现模块化的效果,从而保证不污染全局变量。就如下面这样:

    (function (){// 模块中的代码
    })()
    
  • 为了保证高效的执行,仅加载必要的模块,Node.js只有执行到require函数时才会加载并执行模块(会运行模块内部的代码),并且默认开启了模块缓存,如果模块加载过一次后,会自动使用之前的导出结果;

  • 在模块开执行前,会初始化一个值module.exports = {}module.exports就是模块将要导出的内容;同时,为了方便开发者导出内容,又声明了一个变量exports = module.exports;这一顿骚操作,搞的很多兄弟们就傻傻分不清楚exportsmodule.exports有啥区别。所以,Node.js上来就给我们的模块添加了这样的一坨代码:

(function (){  module.exports = {};  var exports = module.exports;  // module.exports 和 exports指向的是同一个地方  // 模块中的代码......  return module.exports;  
})()

模块最后返回的是module.exports,而不是exports。我们在开发时,要么直接给module.exports赋值,进行导出;要么就给exports以添加字段的方式导出;切勿将一个对象直接赋值给exports,这样就会导致exports和module.exports不是指向同一片内存区域,导致模块无法导出。

AMD规范

其实就这样来看,使用CommonJS规范也没有多大问题,但是由于CommonJS是同步的,必须要等到加载完文件并执行完之后才能继续向后执行。每当一个模块require一个子模块的时候,都会停止当前模块的解析直到子模块读取解析并加载。这就导致在浏览器上很影响性能,很影响使用体验;同时,市面上的浏览器厂商五花八门,他们觉得CommonJS不是官方的标准,是社区的标准,所以不愿意支持。

这就导致AMD规范横空出世了,AMD规范专注于浏览器端。AMD全称是Asynchronous Module Definition,即异步模块加载机制,require.js实现了AMD规范。在AMD中,导入和导出模块都必须放在define函数中。

define([要依赖的模块列表], function(导入的模块名称列表){  // 模块内部的代码  return 导出的内容  
})

至于require.js的使用,这里就不展开细说了,后续会专门写篇文章进行总结。这里咱只要知道为啥有了这么个AMD规范,以及require.js实现了AMD规范即可。

CMD规范

CMD:Common Module Definition_, 通用模块定义。与 AMD 规范类似,也是用于浏览器端,异步加载模块,一个文件就是一个模块,当模块使用时才会加载执行。其语法与 AMD 规范很类似。这里不做赘述了,如想了解详细内容,可以参考这篇文章:https://mp.weixin.qq.com/s/PysnsP3FnO4eCjXeUJruJw

ES6模块化

江湖嘛,总是会有统一的一天,JavaScript标准委员会也注意到这个模块的混乱问题,所以ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。ES6模块化即是未来。至于这个ES6模块化语法,后续专文总结。

技术的演进

根据上面的总结,我们知道使用ES6模块化明显更符合JS的开发,随着web的发展,任何一个支持JS的环境,最终都将会支持ES6的模块化的标准。但是,web端受限于用户使用的浏览器版本,并不能随时使用JS的最新特性。为了能让新代码也能运行在用户的低版本了浏览器上,社区里也有很多工具,它们能静态将高版本规范的代码编译为低版本的代码,比较熟知的就是babel

但是,对于模块化相关的importexport关键字,babel最终会将它编译为包含requireexports的CommonJs规范。

这就有了一个新的问题,这样带有模块化关键词的模块,编译折后还是没有办法直接运行在浏览器中,因为浏览器端并不能运行CommonJS的模块。为了能在web端直接使用CommonJS规范的模块,除了编译之外,我们还需要一个步骤,就是打包(bundle)

总结

通过这篇文章,旨在让大家对JavaScript的模块化演进有一个整体的认识,不要在代码的语法海洋中迷失,能更好的把握代码,理解代码。简单归纳总结一下,就是:

(1) CommonJS => NodeJS(服务器端实现)、Browserify(浏览器端实现)
(2) AMD => requireJS
(3) CMD => seaJS

最终,我们都会在ES6模块化这里统一。适可而止,浅尝辄止。

一个兜兜转转,从“北深”回到三线城市的小码农,热爱生活,热爱技术,在这里和大家分享一个技术人员的点点滴滴。欢迎大家关注我的微信公众号:果冻想

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/801206.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Transformer算法详解

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f366; 参考文章&#xff1a;TensorFlow入门实战&#xff5c;第3周&#xff1a;天气识别&#x1f356; 原作者&#xff1a;K同学啊|接辅导、项目定制 一、文本输入人类理解 词向量&#xff08;E…

ICLR24_OUT-OF-DISTRIBUTION DETECTION WITH NEGATIVE PROMPTS

摘要 分布外检测&#xff08;OOD Detection&#xff09;的研究对于开放世界&#xff08;open-world&#xff09;学习非常重要。受大模型&#xff08;CLIP&#xff09;启发&#xff0c;部分工作匹配图像特征和提示来实现文本-图像特征之间的相似性。 现有工作难以处理具有与已…

Java常用API_System——常用方法及代码演示

1.System.exit(int status) 方法的形参int status为状态码&#xff0c;如果是0&#xff0c;说明虚拟机正常停止&#xff0c;如果非0&#xff0c;说明虚拟机非正常停止。需要将程序结束时可以调用这个方法 代码演示&#xff1a; public class Test {public static void main(S…

第四百四十七回

文章目录 1. 概念介绍2. 思路与方法2.1 实现思路2.2 使用方法 3. 内容总结 我们在上一章回中介绍了"如何在页面上显示蒙板层"相关的内容&#xff0c;本章回中将介绍overlay_tooltip这个三方包.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1. 概念介绍 我们在本…

【SCI绘图】【热力图系列2 R】多特征相关性分析热力图指定聚类 R

SCI&#xff0c;CCF&#xff0c;EI及核心期刊绘图宝典&#xff0c;爆款持续更新&#xff0c;助力科研&#xff01; 本期分享&#xff1a; 【SCI绘图】【热力图系列2 R】多特征相关性分析热力图指定聚类 R 1.环境准备 library(gplots) library(RColorBrewer)2.数据读取 ###…

python小项目——时钟模拟

钟表是一种计时的装置&#xff0c;也是计量和指示时间的精密仪器。钟表的样式千变万化&#xff0c;但是用来显示时间的表盘相差无几&#xff0c;大多数钟表表盘的样式由刻度&#xff08;共60个&#xff0c;围成圆形&#xff09;、指针&#xff08;时针、分针和秒针&#xff09;…

SpringBoot中这样用ObjectMapper,才够优雅!

目录 背景步骤在SpringBoot项目中要实现对象与Json字符串的互转&#xff0c;每次都需要像如下一样new 一个ObjectMapper对象&#xff1a;这样的代码到处可见&#xff0c;有问题吗&#xff1f;我们要使用jmh测试几种方式的区别&#xff1a;所以在我们真正使用的时候不要在方法中…

2024年3月30日~2024年4月7日周报

文章目录 一、前言二、创意收集2.1 多任务学习2.1.1 多任务学习的定义与优势2.1.2 多任务学习的分类 2.2 边缘检测2.2.1 基础理论2.2.2 sobel代码介绍2.2.3 canny代码介绍 三、《地震速度模型超分辨率的多任务学习》3.1 M-RUDSR架构3.2 详细介绍3.3 实验设置 四、实验五、小结5…

【LeetCode题解】2009. 使数组连续的最少操作数

文章目录 [2009. 使数组连续的最少操作数](https://leetcode.cn/problems/minimum-number-of-operations-to-make-array-continuous/)思路&#xff1a;一、排序去重滑动窗口代码&#xff1a; 2009. 使数组连续的最少操作数 思路&#xff1a;一、排序去重滑动窗口 1.对数组进行…

nandgame中的asm编程 Escape Labyrinth(逃离迷宫)

先翻译题目&#xff1a; 逃离迷宫计算机被困在火星上的迷宫中。编写一个程序&#xff0c;让它逃离迷宫。计算机配备了连接的轮子和前方障碍物探测器。与轮子和探测器的输入/输出是内存映射在地址7FFF上&#xff1a;对外设的输出信号&#xff1a; 位 设置为1代表&#xff1a; 2…

UE4_动画基础_ 使用分层动画(Using Layered Animations)

完成在移动过程中武器发射的角色制作&#xff01; 动画混合仅仅意味着在一个角色或骨架网格体上的两个或多个动画之间进行平滑过渡。在虚幻引擎4中&#xff0c;有多种方法可以应用这种混合&#xff0c;要么通过混合空间&#xff0c;或通过实际组合两个基于加权偏差或alpha值的…

Java数组详解

​TOC 第一章、数组的概念介绍 1.1&#xff09;数组的概念 ①数组就是用来储存数据的容器,可以存储同一种类型的数据&#xff0c;是同一种数据类型的集合。实现对这些数据的统一管理。如果数组中存储的是基本类型数据&#xff0c;我就不能往里面存引用类型数据。数组中存储的…

CLIPSeg如果报“目标计算机积极拒绝,无法连接。”怎么办?

CLIPSeg这个插件在使用的时候&#xff0c;偶尔会遇到以下报错&#xff1a; Error occurred when executing CLIPSeg: (MaxRetryError("HTTPSConnectionPool(hosthuggingface.co, port443): Max retries exceeded with url: /CIDAS/clipseg-rd64-refined/resolve/main/toke…

14届蓝桥杯 C/C++ B组 T5 接龙排序 (最长上升子序列DP+优化)

不难发现这是一个LIS问题&#xff0c;但是如果直接套用LIS的模版&#xff0c;在数据范围到达 1 e 5 1e5 1e5 的情况下&#xff0c;就只能够得到一半的分数&#xff0c;所以我们需要对其进行优化。 首先给出暴力的代码&#xff1a; #include<iostream> using namespace…

知识管理系统|基于Springboot和vue的知识管理系统设计与实现(源码+数据库+文档)

知识管理 目录 基于Springboot和vue的知识管理系统设计与实现 一、前言 二、系统设计 三、系统功能设计 1、前台&#xff1a; 5.2.2 文章信息 5.3.1 论坛交流 2、后台 用户管理 5.1.2 文章分类 5.2.1 资料分类 四、数据库设计 五、核心代码 六、论文参考 七、最…

2024年上半年WSK-PETS5报名及考试时间公布

4月1日&#xff0c;中国教育考试网发布了2024年上半年全国外语水平考试WSK&#xff08;PETS5&#xff09;的报名及考试通知&#xff0c;为方便关注者&#xff0c;知识人网小编特做全文转载。 国家公派留学人员全国外语水平考试&#xff08;WSK-PETS5&#xff09;成绩作为国家留…

Python零基础从小白打怪升级中~~~~~~~流程控制语句

第三节&#xff1a;Python的流程控制语法 一、Python条件语句的语法 if 条件1:条件1成立执⾏的代码一条件1成⽴执⾏的代码二...... elif 条件2&#xff1a;条件2成立执⾏的代码三条件2成立执⾏的代码四...... ...... else:以上条件都不成⽴&#xff0c;执行的代码五以上条件都…

axios是什么?axios使用axios和ajax

Axios 是一个基于 Promise 的 HTTP 客户端&#xff0c;用于浏览器和 Node.js 环境。它是由 GitHub 用户 mzabriskie 开发的&#xff0c;并且得到了广泛的社区支持。Axios 的设计目标是提供一种简洁、易用且功能强大的 HTTP 请求方式&#xff0c;以替代传统的 Ajax&#xff08;A…

【深入理解Java IO流0x03】解读Java最基本的IO流之字节流InputStream、OutputStream

在开始前&#xff0c;我们再来回顾一下这张图&#xff1a; 本篇博客主要为大家讲解字节流。 我们都知道&#xff0c;一切文件&#xff08;文本、视频、图片&#xff09;的数据都是以二进制的形式存储的&#xff0c;传输时也是。所以&#xff0c;字节流可以传输任意类型的文件数…

2.动态库与静态库

1.库的制作 库文件是计算机上的一类文件&#xff0c;可以将库文件看做是一种代码仓库。它提供给使用者一些可以直接拿来用的变量&#xff0c;函数或类。库是一种特殊的程序&#xff0c;但是库是不能单独运行的。 库文件有两种&#xff1a;静态库和动态库 静态库: GCC进行链接…