什么是CommonJS规范

发布时间:2021-10-25 11:40:08 作者:iii
来源:亿速云 阅读:255

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

一.什么是模块化?

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

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

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

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

二.JavaScript设计缺陷

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

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

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

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

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

三.没有模块化的问题

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

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进行了支持和实现,所以它具备以下几个特点;

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

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变量

什么是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

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

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

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

什么是CommonJS规范

模块之间的引用关系

什么是CommonJS规范

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

我们加入定时器看看

什么是CommonJS规范

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

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

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中的代码只会运行一次。

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

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

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

什么是CommonJS规范

多个模块的引入关系

四.module.exports

4.1 真正导出的是module.exports

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

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

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规范

什么是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中导入的结果。

什么是CommonJS规范

五.CommonJS的加载过程

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

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

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

六.CommonJS规范的本质

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

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

推荐阅读:
  1. CommonJS,AMD,CMD规范
  2. commonjs指的是什么

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

commonjs node

上一篇:linux如何构建更小的容器镜像

下一篇:Python爬虫经常会被封的原因是什么

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》