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模块化这里统一。适可而止,浅尝辄止。

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

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

嵌入式会越来越卷吗?

当谈及嵌入式系统时&#xff0c;我们探究的不仅是一种科技&#xff0c;更是一个日益多元与普及的趋势。嵌入式系统&#xff0c;作为一种融入更大系统中的计算机硬件和软件&#xff0c;旨在执行特定功能或任务。但这个看似特定的系统概念&#xff0c;却在发展中展现出了惊人的灵…

C++设计模式:观察者模式(三)

1、定义与动机 观察者模式定义&#xff1a;定义对象间的一种1对多&#xff08;变化&#xff09;的依赖关系&#xff0c;以便当一个对象&#xff08;Subject&#xff09;的状态发生比改变时&#xff0c;所有依赖于它的对象都得到通知并且自动更新 再软件构建过程中&#xff0c…

(文章复现)基于改进秃鹰算法的微电网群经济优化调度研究

参考文献&#xff1a; [1]周辉,张玉,肖烈禧,等.基于改进秃鹰算法的微电网群经济优化调度研究[J].太阳能学报,2024,45(02):328-335. 1.基本原理 微电网群由3个独立的微电网(microgrid , MG)组成,各微电网内部包含光伏(photovoltaic , PV)、风力发电机(windturbine,WT)、电动汽…

h5 笔记3 多媒体素材运用

关于电影编码 我们经常用扩展名来判断文件的类型&#xff0c;但是对于影音文件未必适用&#xff0c;影音文件的文件格式(container)和编码(codec)之间并非绝对相关。决定影音文件播放的关键在于浏览器是否含有适合的影音编解码技术。 笔记来源&#xff1a; ©《HTML5CSS3J…

机器学习笔记 - 深度学习遇到超大图像怎么办?使用 xT 对极大图像进行建模论文简读

作为计算机视觉研究人员,在处理大图像时,避免不了受到硬件的限制,毕竟大图像已经不再罕见,手机的相机和绕地球运行的卫星上的相机可以拍摄如此超大的照片,遇到超大图像的时候,我们当前最好的模型和硬件都会达到极限。 所以通常我们在处理大图像时会做出两个次优选择之一:…

Centos 7 安装通过yum安装google浏览器

在CentOS 7上使用yum安装Google Chrome浏览器稍微复杂一些&#xff0c;因为Chrome并不直接包含在默认的Yum仓库中。按照以下步骤来操作&#xff1a; 1、添加Google Chrome仓库 首先&#xff0c;您需要手动添加Google Chrome的Yum仓库。打开终端&#xff0c;并使用文本编辑器&a…

物联网实战--驱动篇之(四)LoRa应用(modbus)

目录 一、前言 二、板级收发 三、主机请求 四、从机接收及回复 五、主机接收 一、前言 之前两篇分别介绍了modbus和sx1278的驱动&#xff0c;但是都并未具体讲解如何应用&#xff0c;那么这一篇就把两者结合起来&#xff0c;做个小demo&#xff0c;便于理解这两个驱动的使…

2005-2023年各省国内生产总值指数分季度数据

2005-2023年各省国内生产总值指数分季度数据 1、时间&#xff1a;2005-2023年 2、来源&#xff1a;国家统计局、各省统计局 3、指标&#xff1a;地区生产总值指数(上年同期100)_累计值(%) 4、范围&#xff1a;31省 5、时间跨度&#xff1a;季度 6、缺失情况&#xff1a;无…

20240325-1-HMM

HMM 直观理解 马尔可夫链&#xff08;英语&#xff1a;Markov chain&#xff09;&#xff0c;又称离散时间马尔可夫链&#xff08;discrete-time Markov chain&#xff0c;缩写为DTMC&#xff09;&#xff0c;因俄国数学家安德烈马尔可夫&#xff08;俄语&#xff1a;Андре…

Ubuntu 20.04.06 PCL C++学习记录(十六)

[TOC]PCL中点云分割模块的学习 学习背景 参考书籍&#xff1a;《点云库PCL从入门到精通》以及官方代码PCL官方代码链接,&#xff0c;PCL版本为1.10.0&#xff0c;CMake版本为3.16 学习内容 用一组点云数据做简单的平面的分割 源代码及所用函数 源代码 #include<iostr…

Linux 内核:线程的实现

在linux中的线程是轻量级线程&#xff08;Light-Weight-process&#xff0c;LWP&#xff09; 文章目录 线程概念线程实现 线程概念 线程分类 用户级线程内核级线程&#xff0c;没有用户空间&#xff0c;完全工作在内核中&#xff08;下图中没有[]的就是用户级线程&#xff09…

跨服务器迁移nextcloud数据

背景 阿里云最近做活动,99一年的2U2G的服务器,比我原来的1U1G的服务器不知道高到哪里去了,于是决定迁移服务器数据到另一台主机上。原先的计划是直接做一个自定义镜像,然后复制到另一台主机就行,结果发现旧主机是aliyunOS,新主机不想踩这个坑了,决定换成乌班图,因此决定重新搭…

大模型基础知识 - 语言模型及其演进

开场白 人工智能发展到现在&#xff0c;在2个重要领域取得了重大突破&#xff0c;有望达到人类水平&#xff1a; 计算机视觉 &#xff08;Computer Vision, CV&#xff09;-> 希望机器帮助人类处理图像数据自然语言处理&#xff08;Natural Language Processing, NLP&…

基于R语言lavaan结构方程模型(SEM)实践技术应用

原文链接&#xff1a;基于R语言lavaan结构方程模型&#xff08;SEM&#xff09;实践技术应用https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247596681&idx4&sn08753dd4d3e7bc492d750c0f06bba1b2&chksmfa823b6ecdf5b278ca0b94213391b5a222d1776743609cd…

文章自动生成器

1.前言 最近开始想写一些东西&#xff0c;脑子里也有不少想法&#xff0c;但是自己写又比较费时间&#xff0c;而且还要排版&#xff0c;精修&#xff0c;刚开始写的字数甚少&#xff0c;想法也是断断续续&#xff0c;不连贯&#xff0c;本想放弃&#xff0c;但是想到放弃就太…

HarmonyOS实战开发-如何实现跨应用数据共享实例。

介绍 本示例实现了一个跨应用数据共享实例&#xff0c;分为联系人&#xff08;数据提供方&#xff09;和联系人助手&#xff08;数据使用方&#xff09;两部分&#xff1a;联系人支持联系人数据的增、删、改、查等功能&#xff1b;联系人助手支持同步联系人数据&#xff0c;当…

15.队列集

1.简介 在使用队列进行任务之间的“沟通交流”时&#xff0c;一个队列只允许任务间传递的消息为同一种数据类型&#xff0c;如果需要在任务间传递不同数据类型的消息时&#xff0c;那么就可以使用队列集。FreeRTOS提供的队列集功能可以对多个队列进行“监听”&#xff0c;只要…

【数据结构与算法】—— 二叉树

目录 一、树 1、初识树 2、树的一些概念 3、树的表示形式 二、二叉树 1、初识二叉树 2、两种特殊的二叉树 3、二叉树的性质 4、二叉树的遍历 5、实现一棵二叉树 6、二叉树题目&#xff08;没代码的后面会给补上&#xff09; 一、树 1、初识树 &#xff08;1&…

从人机界面设计黄金三法则视角看 ChatGPT 的界面设计的“好”与“坏”

热门文章推荐&#xff1a; &#xff08;1&#xff09;《为什么很多人工作 3 年 却只有 1 年经验&#xff1f;》&#xff08;2&#xff09;《一文掌握大模型提示词技巧&#xff1a;从战略到战术巧》&#xff08;3&#xff09;《AI 时代&#xff0c;程序员的出路在何方&#xff1…

破晓数据新纪元:隐语隐私计算,携手共创安全智能的未来生态

1.业务背景&#xff1a;安全核对产生的土壤 隐语隐私计算在安全核对业务背景下的应用&#xff0c;主要聚焦于解决企业在数据交换和分析过程中面临的隐私保护问题。 在许多行业中&#xff0c;特别是在金融、医疗、政务等领域&#xff0c;数据的安全核对至关重要&#xff0c;例如…