commonjs的导出是值拷贝吗?

近期学习nodejs时,我发现很多网上比较commonjs和esmodule不一样之处的文章都提到commonjs的导出是值拷贝,导出值改变不会致使导入值改变,esmodule的导出是导出引用地址,导出值改变则导入值同时改变。 真的是这样吗?因而我试了试,发现问题没有那么简单。node

实验环境说明

实验的目录环境以下webpack

src
  commonjs
    - index.js
    - lib.js
  esmodule
    - index.js
    - lib.js  
复制代码

执行环境是nodejs 12.10.0web

代码用nodejs直接执行一次bash

用webpack打包后nodejs执行一次(webpack和nodejs执行结果一致,下文就不区分是哪一种执行结果了。)ide

其中nodejs 使用esmodule的命令 node --experimental-modules index.js学习

webpack 打包命令 node --experimental-modules index.jsui

commonjs代码及执行结果

// index.js
const { ss } = require('./lib');
const lib = require('./lib');
console.log('ss', ss);
console.log('lib', lib);
setTimeout(()=>{
    console.log('ss', ss);
    console.log('lib', lib);
},3000);
复制代码
// lib.js
module.exports.ss = 'ss1';
setTimeout(()=>{
    module.exports.ss = 'ss2';
    console.log('module.exports', module.exports);
},2000);

复制代码
执行结果
ss ss1
lib { ss: 'ss1' }
lib module.exports { ss: 'ss2' }
ss ss1
lib { ss: 'ss2' }
复制代码
// webpack打包后的相关代码
/******/ ({
/***/ "./index.js":
/***/ (function(module, exports, __webpack_require__) {

const { ss } = __webpack_require__(/*! ./lib */ "./lib.js");
const lib = __webpack_require__(/*! ./lib */ "./lib.js");
console.log('ss', ss);
console.log('lib', lib);
setTimeout(()=>{
    console.log('ss', ss);
    console.log('lib', lib);
},3000);
/***/ }),

/***/ "./lib.js":
/***/ (function(module, exports) {

module.exports.ss = 'ss1';
setTimeout(()=>{
    module.exports.ss = 'ss2';
    console.log('module.exports', module.exports);
},2000);
/***/ })
/******/ });
复制代码

从执行结果能够看出spa

  1. commonjs 导出的是module.exports这个对象,导出值给这个对象添加新的属性会影响导入值。
  2. const { ss } = require('./lib'); 至关于 const { ss } = {ss:'ss1'}; 解构赋值,至关于const ss = 'ss1';因此导出对象修改ss不能使导入对象ss也变成2。

阶段性结论

commonjs的导出是值拷贝这句话是错误的,commonjs导出的是module.exports,commonjs的导入就是变量赋值。当module.exports的值是字符串、数字等原始类型时,赋值是值拷贝才会产生导出值改变不会致使导入值改变的现象。指针

esmodule代码及执行结果

彷佛话题到此为止,可是我又产生了一个疑惑,为何会有commonjs的导出是值拷贝这个提法呢?因而我又实验了一下esmodule,发现了一些有趣的东西。code

// index.js
import {a} from './lib.js';
import b from './lib.js';
console.log('a',a);
console.log('b',b);

setTimeout(()=>{
    console.log('a',a);
    console.log('b',b);
}, 3000);
复制代码
// lib.js
export let a = 1;
let b = 1;
export default b;
setTimeout(()=>{
    a = 2;
    b = 2;
    console.log('a', a);
    console.log('b', b);
}, 2000);
复制代码
执行结果
a 1
b 1
lib a 2
lib b 2
a 2
b 1
复制代码
// webpack 导出文件
/******/ ({
/***/ "./index.js":
/*! no exports provided */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
 "use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _lib_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./lib.js */ "./lib.js");


console.log('a',_lib_js__WEBPACK_IMPORTED_MODULE_0__["a"]);
console.log('b',_lib_js__WEBPACK_IMPORTED_MODULE_0__["default"]);

setTimeout(()=>{
    console.log('a',_lib_js__WEBPACK_IMPORTED_MODULE_0__["a"]);
    console.log('b',_lib_js__WEBPACK_IMPORTED_MODULE_0__["default"]);
}, 3000);


/***/ }),

/***/ "./lib.js":
/*! exports provided: a, default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
 "use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return a; });
let a = 1;
let b = 1;
/* harmony default export */ __webpack_exports__["default"] = (b);
setTimeout(()=>{
    a = 2;
    b = 2;
    console.log('lib a', a);
    console.log('lib b', b);
}, 2000);
/***/ })
/******/ });
复制代码

从执行结果能够看出

  1. 和commonjs不一样,即便导出文件导出数字,当导出值变化时,导入值也变化。
  2. esmodule 导出了一个对象,可是没有办法在导入文件中直接引用这个对象,只能拿到这个对象中的属性。
  3. esmodule 导入导出值都指向同一个同一个内存地址,因此导入值会跟随导出值变化。

结论

commonjs导出的是module.exports,commonjs的导入就是变量赋值。当module.exports的值是字符串、数字等原始类型时,赋值是值拷贝才会产生导出值改变不会致使导入值改变的现象。 esmodule中的导入值更像一个指针,导入导出值都指向同一个同一个内存地址,因此导入值会随导出值变化而变化。

相关文章
相关标签/搜索