返回顶部
首页 > 资讯 > 前端开发 > JavaScript >什么是CommonJS规范
  • 259
分享到

什么是CommonJS规范

2024-04-02 19:04:59 259人浏览 泡泡鱼
摘要

这篇文章主要介绍“什么是Commonjs规范”,在日常操作中,相信很多人在什么是CommonJS规范问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”什么是CommonJS规范”

这篇文章主要介绍“什么是Commonjs规范”,在日常操作中,相信很多人在什么是CommonJS规范问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”什么是CommonJS规范”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

一.什么是模块化?

在很多开发的情况下,我们都知道要使用模块化开发,那为什么要使用它呢?

而事实上,模块化开发最终的目的是将程序划分成一个个小的结构;

  • 在这个结构中编写属于自己的逻辑代码,有自己的作用域,不会影响到其他的结构;

  • 这个结构可以将自己希望暴露的变量、函数、对象等导出给其结构使用;

  • 也可以通过某种方式,导入另外结构中的变量、函数、对象等;

上面说提到的结构,就是模块;

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

二.JavaScript设计缺陷

在网页开发的早期,由于javascript仅仅作为一种脚本语言,只能做一些简单的表单验证或动画实现等,它还是具有很多的缺陷问题的,比如:

  • var定义的变量作用域问题;

  • JavaScript的面向对象并不能像常规面向对象语言一样使用class;

  • 在早期JavaScript并没有模块化的问题,所以也就没有对应的模块化解决方案;

但随着前端和JavaScript的快速发展,JavaScript代码变得越来越复杂了;

  • ajax的出现,前后端开发分离,意味着后端返回数据后,我们需要通过JavaScript进行前端页面的渲染;

  • SPA的出现,前端页面变得更加复杂:包括前端路由、状态管理等等一系列复杂的需求需要通过JavaScript来实现;

  • 包括node的实现,JavaScript编写复杂的后端程序,没有模块化是致命的硬伤;

所以,模块化已经是JavaScript一个非常迫切的需求:

  • 但是JavaScript本身,直到es6(2015)才推出了自己的模块化方案;

  • 在此之前,为了让JavaScript支持模块化,涌现出了很多不同的模块化规范:AMD、CMD、CommonJS等;

到此,我们明白了为什么要用模块化开发?

那如果没有模块化会带来什么问题呢?

三.没有模块化的问题

当我们在公司面对一个大型的前端项目时,通常是多人开发的,会把不同的业务逻辑分步在多个文件夹当中。

2.1 没有模块化给项目带来的弊端

假设有两个人,分别是小豪和小红在开发一个项目

项目的目录结构是这样的

什么是CommonJS规范

小豪开发的bar.js文件

var name = "小豪";  console.log("bar.js----", name);

小豪开发的baz.js文件

console.log("baz.js----", name);

小红开发的foo.js文件

var name = "小红";  console.log("foo.js----", name);

引用路径如下:

<body>   <script src="./bar.js"></script>   <script src="./foo.js"></script>   <script src="./baz.js"></script> </body>

最后当我去执行的时候,却发现执行结果:

什么是CommonJS规范

当我们看到这个结果,有的小伙伴可能就会惊讶,baz.js文件不是小豪写的么?为什么会输出小红的名字呢?

究其原因,我们才发现,其实JavaScript是没有模块化的概念(至少到现在为止还没有用到ES6规范),换句话说就是每个.js文件并不是一个独立的模块,没有自己的作用域,所以在.js文件中定义的变量,都是可以被其他的地方共享的,所以小豪开发的baz.js里面的name,其实访问的是小红重新声明的。

但是共享也有一点不好就是,项目的其他协作人员也可以随意的改变它们,显然这不是我们想要的。

2.2 IIFE解决早期的模块化问题

所以,随着前端的发展,模块化变得必不可少,那么在早期是如何解决的呢?

在早期,因为函数是有自己的作用域,所以可以采用立即函数调用表达式(IIFE),也就是自执行函数,把要供外界使用的变量作为函数的返回结果。

小豪&mdash;&mdash;bar.js

var moduleBar = (function () {   var name = "小豪";   var age = "18";    console.log("bar.js----", name, age);    return {     name,     age,   }; })();

小豪&mdash;&mdash;baz.js

console.log("baz.js----", moduleBar.name); console.log("baz.js----", moduleBar.age);

小红&mdash;&mdash;foo.js

(function () {   var name = "小红";   var age = 20;    console.log("foo.js----", name, age); })();

来看一下,解决之后的输出结果,原调用顺序不变;

什么是CommonJS规范

但是,这又带来了新的问题:

  • 我必须记得每一个模块中返回对象的命名,才能在其他模块使用过程中正确的使用;

  • 代码写起来杂乱无章,每个文件中的代码都需要包裹在一个匿名函数中来编写;

  • 在没有合适的规范情况下,每个人、每个公司都可能会任意命名、甚至出现模块名称相同的情况;

所以现在急需一个统一的规范,来解决这些缺陷问题,就此CommonJS规范问世了。

三.Node模块化开发&mdash;&mdash;CommonJS规范

3.1  CommonJS规范特性

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

  • Node是CommonJS在服务器端一个具有代表性的实现;

  • Browserify是CommonJS在浏览器中的一种实现;

  • webpack打包工具具备对CommonJS的支持和转换;

正是因为Node中对CommonJS进行了支持和实现,所以它具备以下几个特点;

  • 在Node中每一个js文件都是一个单独的模块;

  • 该模块中,包含CommonJS规范的核心变量: exports、module.exports、require;

  • 使用核心变量,进行模块化开发;

无疑,模块化的核心是导出和导入,Node中对其进行了实现:

  • exports和module.exports可以负责对模块中的内容进行导出;

  • require函数可以帮助我们导入其他模块(自定义模块、系统模块、第三方库模块)中的内容;

3.2 CommonJS配合Node模块化开发假设现在有两个文件:

bar.js

const name = "时光屋小豪"; const age = 18;  function sayHello(name) {   console.log("hello" + name); }

main.js

console.log(name); console.log(age);

执行node main.js之后,会看到

什么是CommonJS规范

这是因为在当前main.js模块内,没有发现name这个变量;

这点与我们前面看到的明显不同,因为Node中每个js文件都是一个单独的模块。

那么如果要在别的文件内访问bar.js变量

  • bar.js需要导出自己想要暴露的变量、函数、对象等等;

  • main.js从bar.js引入想用的变量、函数、对象等等;

什么是CommonJS规范

3.3 exports导出

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

bar.js文件导出:

const name = "时光屋小豪"; const age = 18;  function sayHello(name) {   console.log("hello" + name); }  exports.name = name; exports.age = age; exports.sayHello = sayHello;

main.js文件导入:

const bar = require('./bar');  console.log(bar.name);  // 时光屋小豪 console.log(bar.age);   // 18

其中要注意的点:

main.js中的bar变量等于exports对象;

bar = exports
  • 所以我们通过bar.xxx来使用导出文件内的变量,比如name,age;

  • require其实是一个函数,返回值是一个对象,值为“导出文件”的exports对象;

3.4 从内存角度分析bar和exports是同一个对象

在Node中,有一个特殊的全局对象,其实exports就是其中之一。

如果在文件内,不再使用exports.xxx的形式导出某个变量的话,其实exports就是一个空对象。

什么是CommonJS规范

模块之间的引用关系

什么是CommonJS规范

  • 当我们在main.js中require导入的时候,它会去自动查找特殊的全局对象exports,并且把require函数的执行结果赋值给bar;

  • bar和exports指向同一个引用(引用地址相同);

  • 如果发现exports上有变量,则会放到bar对象上,正因为这样我们才能从bar上读取想用的变量;

为了进一步论证,bar和exports是同一个对象:

我们加入定时器看看

什么是CommonJS规范

所以综上所述,Node中实现CommonJS规范的本质就是对象的引用赋值(浅拷贝本质)。

把exports对象的引用赋值bar对象上。

  • CommonJS规范的本质就是对象的引用赋值

3.5 module.exports又是什么?

但是Node中我们经常使用module.exports导出东西,也会遇到这样的面试题

module.exports和exports有什么关系或者区别呢?

3.6 require细节

require本质就是一个函数,可以帮助我们引入一个文件(模块)中导入的对象。

require的查找规则https://nodejs.org/dist/latest-v14.x/docs/api/modules.html#modules_all_together

3.7 require模块的加载顺序

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

// aaa.js const name = 'coderwhy';  console.log("Hello aaa");  setTimeout(() => {   console.log("setTimeout"); }, 1000);
// main.js const aaa = require('./aaa');

aaa.js中的代码在引入时会被运行一次

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

// main.js const aaa = require('./aaa'); const bbb = require('./bbb');
/// aaa.js const ccc = require("./ccc");
// bbb.js const ccc = require("./ccc");
// ccc.js console.log('ccc被加载');

ccc中的代码只会运行一次。

为什么只会加载运行一次呢?

  • 每个模块对象module都有一个属性:loaded;

  • 为false表示还没有加载;

  • 为true表示已经加载;

结论三:如果有循环引入,那么加载顺序是什么?

如果出现下面模块的引用关系,那么加载顺序是什么呢?

  • 这个其实是一种数据结构:图结构;

  • 图结构在遍历的过程中,有深度优先搜索(DFS, depth first search)和广度优先搜索(BFS, breadth first  search);

  • Node采用的是深度优先算法:main -> aaa -> ccc -> DDD -> eee ->bbb;

什么是CommonJS规范

多个模块的引入关系

四.module.exports

4.1 真正导出的是module.exports

以下是通过维基百科对CommonJS规范的解析:

  • CommonJS中是没有module.exports的概念的;

  • 但是为了实现模块的导出,Node中使用的是Module的类,每一个模块都是Module的一个实例module;

  • 所以在Node中真正用于导出的其实根本不是exports,而是module.exports;

  • exports只是module上的一个对象

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

  • 这是因为module对象的exports属性是exports对象的一个引用;

  • 等价于module.exports = exports = main中的bar(CommonJS内部封装);

4.2 module.exports和exports有什么关系或者区别呢?

联系:module.exports = exports

进一步论证module.exports = exports

// bar.js const name = "时光屋小豪";  exports.name = name;  setTimeout(() => {   module.exports.name = "哈哈哈";   console.log("bar.js中1s之后", exports.name); }, 1000);
// main.js const bar = require("./bar");  console.log("main.js", bar.name);  setTimeout((_) => {   console.log("main.js中1s之后", bar.name); }, 2000);

什么是CommonJS规范

在上面代码中,只要在bar.js中修改exports对象里的属性,导出的结果都会变,因为即使真正导出的是  module.exports,而module.exports和exports是都是相同的引用地址,改变了其中一个的属性,另一个也会跟着改变。

注意:真正导出的模块内容的核心其实是module.exports,只是为了实现CommonJS的规范,刚好module.exports对exports对象使用的是同一个引用而已

什么是CommonJS规范

区别:有以下两点

那么如果,代码这样修改了:

什么是CommonJS规范

  • module.exports 也就和 exports没有任何关系了;

  • 无论exports怎么改,都不会影响最终的导出结果;

  • 因为module.exports = { xxx  }这样的形式,会在堆内存中新开辟出一块内存空间,会生成一个新的对象,用它取代之前的exports对象的导出

  • 那么也就意味着require导入的对象是新的对象;

什么是CommonJS规范

图解module.exports和exports的区别

讲完它们两个的区别,来看下面这两个例子,看看自己是否真正掌握了module.exports的用法

4.3 关于module.exports的练习题

练习1:导出的变量为值类型

// bar.js let name = "时光屋小豪";  setTimeout(() => {   name = "123123"; }, 1000);  module.exports = {   name: name,   age: "20",   sayHello: function (name) {     console.log("你好" + name);   }, };
// main.js const bar = require("./bar");  console.log("main.js", bar.name); // main.js 时光屋小豪  setTimeout(() => {   console.log("main.js中2s后", bar.name); // main.js中2s后 时光屋小豪 }, 2000);

练习2:导出的变量为引用类型

// bar.js let info = {   name: "时光屋小豪", };  setTimeout(() => {   info.name = "123123"; }, 1000);  module.exports = {   info: info,   age: "20",   sayHello: function (name) {     console.log("你好" + name);   }, };
// main.js const bar = require("./bar");  console.log("main.js", bar.info.name); // main.js 时光屋小豪  setTimeout(() => {   console.log("main.js中2s后", bar.info.name); // main.js中2s后 123123 }, 2000);

从main.js输出结果来看,定时器修改的name变量的结果,并没有影响main.js中导入的结果。

  • 因为name为值类型,基本类型,一旦定义之后,就把其属性值,放到了module.exports的内存里(练1)

  • 因为info为引用类型,所以module.exports里存放的是info的引用地址,所以由定时器更改的变量,会影响main.js导入的结果(练2)

什么是CommonJS规范

五.CommonJS的加载过程

CommonJS模块加载js文件的过程是运行时加载的,并且是同步的:

  • 运行时加载意味着是js引擎在执行js代码的过程中加载模块;

  • 同步的就意味着一个文件没有加载结束之前,后面的代码都不会执行;

const flag = true;  if (flag) {   const foo = require('./foo');   console.log("等require函数执行完毕后,再输出这句代码"); }

CommonJS通过module.exports导出的是一个对象:

  • 导出的是一个对象意味着可以将这个对象的引用在其他模块中赋值给其他变量;

  • 但是最终他们指向的都是同一个对象,那么一个变量修改了对象的属性,所有的地方都会被修改;

六.CommonJS规范的本质

CommonJS规范的本质就是对象的引用赋值

到此,关于“什么是CommonJS规范”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注编程网网站,小编会继续努力为大家带来更多实用的文章!

--结束END--

本文标题: 什么是CommonJS规范

本文链接: https://lsjlt.com/news/84356.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 什么是CommonJS规范
    这篇文章主要介绍“什么是CommonJS规范”,在日常操作中,相信很多人在什么是CommonJS规范问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”什么是CommonJS规范”...
    99+
    2024-04-02
  • js模块化CommonJS、AMD、UMD、CMD和ES6规范是什么
    这篇“js模块化CommonJS、AMD、UMD、CMD和ES6规范是什么”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“js...
    99+
    2023-07-06
  • Node.js中的CommonJS模块化规范详解
    目录nodeJS的介绍CommonJS模块不止 .jsrequire 导入模块的查找机制导入内置模块导入第三方模块小结nodeJS的介绍 NodeJS不但自己可以完成服务器软件的功能...
    99+
    2023-02-06
    Node.js CommonJS模块化规范 Node.js 模块化规范 Node.js 模块化  CommonJS
  • Node.js CommonJS 规范与 ES 模块:大比拼
    语法 CommonJS 规范:使用 require() 语句加载模块,模块通常是 .js 文件。 ES 模块:使用 import 和 export 语句加载和导出模块,模块通常是 .mjs 文件。 作用域 CommonJS 规范:模...
    99+
    2024-03-11
    Node.js 的模块系统经历了两次主要演变:CommonJS 规范和 ES 模块。这两种规范在语法、作用域和加载机制方面存在显着差异 理解这些差异对于选择最适合特定项目的模块系统至关重要。
  • Git提交规范是什么
    本篇内容介绍了“Git提交规范是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!一、为什么需要规范无规矩...
    99+
    2024-04-02
  • python中什么是WSGI规范
    这期内容当中小编将会给大家带来有关python中什么是WSGI规范,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。python主要应用领域有哪些1、云计算,典型应用OpenStack。2、WEB前端开发,众...
    99+
    2023-06-14
  • 深入理解Commonjs规范及Node模块实现
    前面的话 Node在实现中并非完全按照CommonJS规范实现,而是对模块规范进行了一定的取舍,同时也增加了少许自身需要的特性。本文将详细介绍NodeJS的模块实现 引入 nodejs是区别于javasc...
    99+
    2022-06-04
    模块 Commonjs Node
  • HTML的基础规范是什么
    这篇文章主要介绍“HTML的基础规范是什么”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“HTML的基础规范是什么”文章能帮助大家解决问题。 HTML是一个弱势语...
    99+
    2024-04-02
  • html5是属于什么的规范
    这篇文章主要介绍了html5是属于什么的规范,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。html5属于Web中核心语言HTML的规范;ht...
    99+
    2024-04-02
  • MySQL的语法规范是什么
    MySQL的语法规范如下:1. 关键字不区分大小写:MySQL的关键字不区分大小写,但是为了提高代码可读性,建议使用大写字母表示关键...
    99+
    2023-08-20
    MySQL
  • Node.js CommonJS 规范揭秘:模块化开发的基石
    CommonJS 规范是 JavaScript 模块化开发的基石,它提供了一套标准的模块化开发方法,使开发者能够将代码组织成独立的模块,并通过 require 函数加载和使用其他模块。CommonJS 模块化开发具有以下优点: 代码可...
    99+
    2024-02-04
    Node.js CommonJS 模块化 require exports
  • python项目目录规范是什么
    这篇文章主要讲解了“python项目目录规范是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“python项目目录规范是什么”吧!目录结构如下XXX/ |...
    99+
    2024-04-02
  • web前端开发规范是什么
    这篇文章主要为大家展示了“web前端开发规范是什么”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“web前端开发规范是什么”这篇文章吧。 一、css书写规范 ...
    99+
    2024-04-02
  • Java代码格式规范是什么
    本篇内容介绍了“Java代码格式规范是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!   public&n...
    99+
    2023-06-17
  • VB.NET变量范围规则是什么
    这篇文章主要为大家展示了“VB.NET变量范围规则是什么”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“VB.NET变量范围规则是什么”这篇文章吧。VB.NET编程语言会给我们带来什么不一样的感受...
    99+
    2023-06-17
  • python类名定义规范是什么
    Python类名的定义规范通常遵循以下约定:1. 类名应该采用大写字母开头的驼峰命名法,即每个单词的首字母大写,不使用下划线。例如:...
    99+
    2023-09-23
    python
  • python变量赋值规范是什么
    Python变量赋值的规范主要有以下几点:1. 变量名只能包含字母、数字和下划线,且不能以数字开头。2. 变量名不能包含空格和特殊字...
    99+
    2023-09-05
    python
  • labview中dll调用规范是什么
    LabVIEW中调用DLL的规范包括以下几点:1. 函数命名规范:DLL中的函数应该使用简洁明了的名称,以便在LabVIEW中调用。...
    99+
    2023-09-15
    labview
  • PHP 函数命名规范是什么?
    php 函数命名规范为驼峰命名法,遵循以下约定:1) 以动词或动词短语开头;2) 描述函数功能;3) 使用具体而有意义的单词;4) 避免使用缩写;5) 保持简洁。 PHP 函数命名规范...
    99+
    2024-04-20
    php 命名规范
  • 抛开概念,手把手教你玩转 Node.js CommonJS 规范
    一、CommonJS 规范简介 CommonJS 规范是一套用于在 JavaScript 中定义和加载模块的规范。它最早由 Paul Irish 和 Dave Herman 在 2009 年提出,并很快成为 Node.js 和其他 Ja...
    99+
    2024-02-04
    Node.js CommonJS 规范 模块 依赖
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作