zoukankan      html  css  js  c++  java
  • exports、module.exports和export、export default到底是咋回事,区别在哪里

    Node模块

    Node里面的模块系统遵循的是CommonJS规范。

    CommonJS定义的模块分为:

    1、模块标识(module)
    2、模块定义(exports)
    3、模块引用(require)

    先解释 exports 和 module.exports

    在一个node执行一个文件时,会给这个文件内生成一个 exports和module对象, 而module又有一个exports属性。他们之间的关系如下图,都指向一块{}内存区域。

    exports = module.exports = {};
    

    接下来上代码:

    //utils.js
    let test = 'today';
    
    console.log(module.exports); //能打印出结果为:{}
    console.log(exports); //能打印出结果为:{}
    
    exports.test = 'tomorrow'; //这里辛苦劳作帮 module.exports 的内容给改成 {test: 'tomorrow'}
    
    exports = '指向其他内存区'; //然后在这里把exports的指向指到其他地方
    
    //test.js
    
    let _test = require('/utils');
    console.log(_test) // 打印为 {test: 'tomorrow'} 
    

      

    1、由此可见,require导出的内容其实是module.exports的指向的内存块内容({test: 'tomorrow'}),并不是exports的内容('指向其他内存区')。
    2、简而言之,区分他们之间的区别就是 exports 只是 module.exports的引用,exports是辅助module.exports操作内存中的数据用的,结果到最后真正被require出去的内容还是module.exports的。

    为了避免糊涂,尽量都用 module.exports 导出,然后用require导入

    ES中的模块导入和导出

    先解释 export 和 export default

    1、export与export default均可用于导出常量、函数、文件、模块等
    2、在一个文件或模块中,export、import可以有多个,export default仅有一个
    3、通过export方式导出,在导入时要加{ },export default则不需要
    4、export能直接导出变量表达式,export default不行。

    例如下面代码:
    1、export (testExport.js)

    'use strict'

    //导出常量 export const a = '100'; //导出函数方法 export const foo = function(){ console.log('This is a function foo'); } //导出方法第二种 function bar(){ console.log('My name is bar'); } export { bar }; // 相应的导入的方式为 import { foo, bar } from './testExport'; //导出了 export 方法 foo(); // 直接执行foo方法 bar(); // 直接执行bar方法

      

     

    2、export default (testExportDefault.js)

    'use strict'
    //export default导出
    const b = 100;
    export const foo = function(){ 
        console.log('This is a function foo');
    }
    export default b
    //  也可以只导出一个对象
    //  export default {b, foo};   
    
    //export defult const b = 100;// 这里不能写这种格式,是错误的,可以导出一个对象, export default {b}。
    

      

    综合应用:

    //index.js
    'use strict'
    var express = require('express');
    var router = express.Router();
    
    import { foo, too } from './testExport'; //导出了 export 方法 
    import b from './testExportDefault';  //导出了 export default 
    
    import * as testExportModule from './testExportDefault'; //as 集合成对象导出
    
    import * as testExportDefaultModule from './testExportDefault'; //as 集合成对象导出
    
      foo();
      bar();
      testExportModule.foo();
      console.log(b);
      console.log(testExportDefaultModule.b); // undefined , 因为  as 导出是 把 零散的 export 聚集在一起作为一个对象,而export default 是导出为 default属性。
      console.log(testExportDefaultModule.default); // 100 or {b: 100, foo: function}

     module.exports = router; 

     

    补充一下模块化标准规范

    1、模块化的最佳实践

    node.js 环境中,遵循 CommonJS 规范
    浏览器环境中,遵循 ES Modules 规范

    1.1、ES Modules 基本特性

    自动采用严格模式,忽略 'use strict'
    每个 ESM 模块都是单的私有作用域
    ESM 是通过 CORS 去请求外部 JS 模块的
    ESM 的 script 标签会延迟执行脚本

    ES Modules 注意事项

    • export {} 这是一个固定的语法,不是 es6 中的对象简写
    • import {} 这是一个固定的语法,不是 es6 中的对象解构
    • 导出得到的是对值的引用,模块内部修改了值外部也会跟着改变
    • 导入的成员是只读的成员

    1.2、ES Modules 导出和导入

    • export 注意有无 default 关键字
    • 不能省略文件后缀名,不能省略 ./
    • 执行某个模块,不需要提取模块中的成员 import './module.js'
    • 动态导入模块,可以用全局函数 import()

    1.3、ES Modules in Node.js 支持情况:

    • 执行文件时,node --experimental-modules index.mjs
    • ES Module 中可以导入 CommonJS 模块
    • CommonJS 中不能导入 ES Module 模块
    • CommonJS 始终只会导出一个默认成员
    • 注意 import 不是解构导出对象

    ES6 模块和 CommonJS 的差异点:

    • CommonJS 可以在运行时使用变量进行 require, 例如 require(path.join('xxxx', 'xxx.js')),CommonJS 模块是运行时加载,而静态 import 语法(还有动态 import,返回 Promise)不行,因为 ES6模块是编译时输出接口, 模块会先解析所有模块再执行代码。
    • require 会将完整的 exports 对象引入,CommonJS输出的是一个值的拷贝,import 可以只 import 部分必要的内容,ES6 模块输出的是值的引用,这也是为什么使用 Tree Shaking 时必须使用 ES6 模块 的写法。
    • import 另一个模块没有 export 的变量,在代码执行前就会报错,而 CommonJS 是在模块运行时才报错。
    • 因为CommonJS的require语法是同步的,所以就导致了CommonJS模块规范只适合用在服务端,而ES6模块无论是在浏览器端还是服务端都是可以使用的,但是在服务端中,还需要遵循一些特殊的规则才能使用 ;
    • 因为两个模块加载机制的不同,所以在对待循环加载的时候,它们会有不同的表现。CommonJS遇到循环依赖的时候,只会输出已经执行的部分,后续的输出或者变化,是不会影响已经输出的变量。而ES6模块相反,使用import加载一个变量,变量不会被缓存,真正取值的时候就能取到最终的值;
    • 关于模块顶层的this指向问题,在CommonJS顶层,this指向当前模块;而在ES6模块中,this指向undefined;
    • 关于两个模块互相引用的问题,在ES6模块当中,是支持加载CommonJS模块的。但是反过来,CommonJS并不能requireES6模块,在NodeJS中,两种模块方案是分开处理的。

    为什么平时开发可以混写?

    面提到 ES6 模块和 CommonJS 模块有很大差异,不能直接混着写。这和开发中表现是不一样的,原因是开发中写的 ES6 模块最终都会被打包工具处理成 CommonJS 模块,以便兼容更多环境,同时也能和当前社区普通的 CommonJS 模块融合。

    循环加载(circular dependency)

    1、CommonJS的循环加载

    想要搞清楚CommonJS的循环加载问题,首先我们要先大概了解下它的加载原理。CommonJS的一个模块,一般就是一个文件,使用reqiure第一次加载一个模块的时候,就会在内存中生成一个对象。

    {
      id: '...',
      exports: { ... },
      loaded: true,
      ...
    }
    

    CommonJS模块的特性就是加载时执行,当脚本被reqiure的时候,就会全部执行。一旦出现某个模块被"循环加载",就只输出已经执行的部分,还未执行的部分不会输出。

    // a.js
    exports.done = false;
    // 首先输出一个done变量({done:false}),然后开始加载b.js,等待b.js执行完,才会继续执行后面的代码
    var b = require('./b.js');
    console.log('在 a.js 之中,b.done = %j', b.done);
    exports.done = true;
    console.log('a.js 执行完毕');
    // b.js
    exports.done = false;
    var a = require('./a.js'); //此时a里面只有 {done:false}
    console.log('在 b.js 之中,a.done = %j', a.done);
    exports.done = true;
    console.log('b.js 执行完毕');
    // main.js
    var a = require('./a.js');
    var b = require('./b.js');
    console.log('在 main.js 之中, a.done=%j, b.done=%j', a.done, b.done);
    

      

    最后执行结果:

    在 b.js 之中,a.done = false
    b.js 执行完毕
    在 a.js 之中,b.done = true
    a.js 执行完毕
    在 main.js 之中, a.done=true, b.done=true

    2、ES6中的循环加载

    ES6 模块是动态引用,如果使用import加载一个变量,变量不会被缓存,真正取值的时候就能取到最终的值

    // even.js
    import { odd } from './odd'
    export var counter = 0;
    export function even(n) {
      counter++;
      return n === 0 || odd(n - 1);
    }
    // even.js里面的函数even有一个参数n,只要不等于 0,就会减去 1,传入加载的odd()。odd.js也会做类似操作。
    
    // odd.js
    import { even } from './even';
    export function odd(n) {
      return n !== 0 && even(n - 1);
    }
    // 参数n从 10 变为 0 的过程中,even()一共会执行 6 次,所以变量counter等于 6
    > import * as m from './even.js';
    > m.even(10);
    true
    > m.counter
    6
    

      

  • 相关阅读:
    (11)模糊图片操作---均值模糊
    (10)绘制形状和文字
    (9)调整图像的亮度和对比度
    (8)图像混合
    (7)opencv图片内部的基本处理
    C#中的线程池使用(二)
    C#中的线程池使用(一)
    C#中线程的委托
    为PyCharm自动配置作者信息
    为PyCharm配置QT
  • 原文地址:https://www.cnblogs.com/CandyDChen/p/14609313.html
Copyright © 2011-2022 走看看