了解 CommonJs 吗 ? 了解。说说 module.exports 和 exports 有什么区别 ?呃,,这个,,🤔

提到 CommonJs,第一时间其实想到的就是 Node.js 的模块,这种模块化的写法浏览器是不支持的,其实这句话只说对了一半,CommonJs 是一种模块化规范,Node.js 只是实现了这种规范,就比如 Promise A+ 这是一个实现异步的一种规范,JavaScript只是根据这个规范实现了这个功能,它浏览器端和 Node.js z中的实现也是不完全一致,其他语言中也有根据这个规范实现的库。

总的来说,CommonJs 是一种规范,今天来讲讲 Node.js 中的模块化语法,它是一个轻微修改版本的CommonJs(这个描述来自于红宝书第四版),就叫它 Node.js 版本的 CommonJs 吧,其中 module.exports 和 exports 的关联。

复制代码
话不多说了,直接上例子
js 复制代码
// AModule.js
const foo = () => {
	return Math.random(10);
};

// case 1
module.exports = {
	foo
};
// case 2
module.exports.foo = foo;
// case 3 
exports.foo = foo;
复制代码
现在我们简单实现一个 foo 函数,然后通过 Node.js 的模块化语法的这三种语法导出。
js 复制代码
// index.js

const AModule = require('./AModule');
console.log(AModule);

// case 1 
// { foo: [Function: foo] }

// case 2
// { foo: [Function: foo] }

// case 3
// { foo: [Function: foo] }
java 复制代码
以上都是比较正确的写法,在 index.js 中都成功获取到 AModule,但是貌似写法不太一样,module.exports 和 exports 傻傻分不清楚,
js 复制代码
// AModule.js

const a = '1';
const foo = () => {
	return Math.random(10);
};

// case 4
exports = {
	foo,
	a
};
// {} 
// 看着就不正常,结果也确实没有获取到

// case 5

exports.foo = foo;
module.exports.a = a;

// { foo: [Function: foo], a: '1' }
// 看似不太正常,但是 foo 和 a 都成功获取到了

// case 6
exports.foo = foo;
module.exports = {}

看完前面 5 个 case,这个 case 6,相信聪明的你,已经知道打印的是什么了,已经明白了这俩玩意有什么区别了。

这个东西看完,我个人觉得这个是和对象的赋值是类似,继续往下瞧。

js 复制代码
let a = 1;
let b = a;
a = 2;
console.log(b); // 1

let c = { name: "lzb" };
let d = c;
c.name = 'lll';
console.log(d.name); // 'lll'
复制代码
看完这个,可能就又明白了

再看下边的例子
js 复制代码
// AModule.js

// case 1
module.exports = {
	foo
}
// case 2
exports.foo = foo;

// 这两种是肯定生效的
// 从语法的角度上讲,exports. 和 module. 这种语法证明,
// 1. 在这个当前的作用域下,是可以获取到的有 exports 和 module 这个两个变量
// 2. 这两个变量应该都是对象

// so ?
// 那么我们直接 console.log() 一下,让他们亮个相

现在就根据上边的两种 case,分别看一下什么情况;

js 复制代码
// AModule.js case 1
console.log(exports);
// {}
console.log(module);
// {
//   id: '...',
//   path: '...',
//   exports: {},
//   filename: '...',
//   loaded: false,
//   children: [],
//   paths: []
// }

module.exports = {
	foo
}

console.log(exports);
// {}
console.log(module);
// 对于当前影响不重要的使用 ... 省略
// {
//   id: '...',
//   path: '...',
//   exports: { foo: [Function: foo] },
//   filename: '...',
//   loaded: false,
//   children: [],
//   paths: []
// }

这里 module.exports 这个的语法,显然是给 module 的 exports 属性赋值,从这个例子中不难看出,最开始 exports 和 module.exports 都是 {}。 而后来 exports 还是 {}, 但是 module.exports 已经被赋值了,此时 exports 和 module.exports,看似有关联,实际上又发现不出来有什么关联,没有证据啊,你找的是鲁迅,跟我周树人有什关系啊,module.exports 这种语法又是大家最常用的,咱们接着往下看

js 复制代码
// AModule.js case 2
console.log(exports);
// {}
console.log(module);
// {
//   id: '...',
//   path: '...',
//   exports: {},
//   filename: '...',
//   loaded: false,
//   children: [],
//   paths: []
// }

exports.foo = foo;

console.log(exports);
// { foo: [Function: foo] },
console.log(module);
// 对于当前影响不重要的使用 ... 省略
// {
//   id: '...',
//   path: '...',
//   exports: { foo: [Function: foo] },
//   filename: '...',
//   loaded: false,
//   children: [],
//   paths: []
// }

咱们再看一下这种情况,这种写法应该是第二常用的写法,module.exports.foo = foo; 虽然也好使,但是应该很少有人这个么写,开始两者都是 {}, 导出之后,两者都是 { foo: [Function: foo] },现在感觉已经初露端倪了。 大胆想象一下,exports === module.exports 应该是 true,准没错了

js 复制代码
// AModule.js case 1
console.log(exports === module.exports);
// true

module.exports = {
	foo
}

console.log(exports === module.exports);
// false

// AModule.js case 2
console.log(exports === module.exports);
// true

exports.foo = foo;

console.log(exports === module.exports);
// true

这个情况跟我们最开始打印 exports 和 module 的情况如出一辙,由此可以断定,exports 和 module.exports 其实就是 c 和 d 一样, 最开始 exports 的值是指向 module.exports 的引用的。

或者说它俩就是一个引用,exports.foo = foo, 只会改变这个引用的中的值, 而 module.exports = {}, 这种,直接开辟了一个新的引用, 这个 exports 还是指向 module.exports 之前的引用,这个样两者就没有干系了, 所以也就是说,在 AModule.js 的初始化的时候相当于有这样一段代码

js 复制代码
let module = {
	exports: {},
	// ...其他属性
}
let exports = module.exports;

也就是说 exports.a = a, 会更新 module.exports, 最终模块的消费方或者使用方,拿的值是 module.exports 的值

总结:不总结了 评论区帮忙总结一下,我看谁说的对

相关推荐
万少2 分钟前
HarmonyOS Next 弹窗系列教程(3)
前端·harmonyos·客户端
七灵微1 小时前
【后端】单点登录
服务器·前端
持久的棒棒君5 小时前
npm安装electron下载太慢,导致报错
前端·electron·npm
crary,记忆7 小时前
Angular微前端架构:Module Federation + ngx-build-plus (Webpack)
前端·webpack·angular·angular.js
漂流瓶jz8 小时前
让数据"流动"起来!Node.js实现流式渲染/流式传输与背后的HTTP原理
前端·javascript·node.js
SamHou08 小时前
手把手 CSS 盒子模型——从零开始的奶奶级 Web 开发教程2
前端·css·web
我不吃饼干8 小时前
从 Vue3 源码中了解你所不知道的 never
前端·typescript
开航母的李大8 小时前
【中间件】Web服务、消息队列、缓存与微服务治理:Nginx、Kafka、Redis、Nacos 详解
前端·redis·nginx·缓存·微服务·kafka
Bruk.Liu8 小时前
《Minio 分片上传实现(基于Spring Boot)》
前端·spring boot·minio
鱼樱前端9 小时前
Vue3+d3-cloud+d3-scale+d3-scale-chromatic实现词云组件
前端·javascript·vue.js