面试官问:”谈谈import/ export与require/ exports的区别?“

216 阅读4分钟
  • module.exports、exports是commonjs的规范。

  • export、export default是es6规范。

  • require是AMD规范引入方式。

  • import是es6的一个语法标准。

module.exports和exports

module.exports和exports针对基础数据类型是值的拷贝,导出复杂数据类型时浅拷贝该对象

理解概念:

  • module.exports:在module对象中,包含exports属性,而我们就是通过module.exports向外暴露共享成员的。

  • exports:nodejs为了简化向外共享成员的代码,提供的一个新方式,在默认情况下,exports和module.exports指向的是同一个对象。一般通过exports.xxx向外暴露成员,而直接给exports赋值毫无意义(不导出只在模块内生效)。

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

理解本质:

  • module.exports:向外暴露始终是module.exports对象属性值,不管暴露的是基础类型还是引用类型。

    为什么module.exports导出的是一个值?

    因为module.exports默认情况下指向的是一个Object对象,而现在直接赋值给module.exports一个新的数据类型,比如下面例子中导出一个基础类型a,不管是引用类型还是基础类型,导出是什么就是什么。

    image.png

    // test1.js
    const a = 1;
    module.exports = a;
    console.log(exports);// {}
    
    // test2.js
    const a = require('./test1');
    console.log(a);// 1
    
  • exports:exports直接赋值向外暴露成员是毫无意义的,因为不管是exports还是module.exports向外暴露成员最终都是以module.exports为准

    // test1.js
    exports = 1;
    console.log(module.exports);// {}
    
    // test1.js
    const a = require('./test1');
    console.log(a);// {}
    

    为什么exports导出的是一个对象?

    因为module.exports与exports指向的都是同一个对象,exports.xxx的意义就是给对象module.exports内的属性xxx赋值,也就是说exports.xxx等同于module.exports.xxx,而下面的例子就可以解析得清楚了。

    // test1.js
    exports.a = 1;
    console.log(module.exports);// {a:1}
    
    // test2.js
    const a = require('./test1');
    console.log(a);// {a:1}
    

export和export default

export和export default模块输出的是值的引用

// export 和 export default用法
export default fs
export const fs
export function readFile
export {readFile, read}
export * from 'fs'
  • export:一个模块就是一个独立的文件。如果你希望外部能够读取模块内部的某个变量,就必须使用export关键字输出该变量。也就是说可以导出多个属性

    // test1.js 
    export const a = 1;
    export const arr = [1, 2, 3];
    export const fun = () => {
        console.log("fun method!");
    }
    
    // test2.js 
    import { a, arr, fun } from "./test1"
    console.log(a);// 1
    console.log(arr);// [1,2,3]
    fun();// fun method!
    
  • export default:为模块指定默认输出,有且仅有一个属性导出。注意地,如果export与export default同时存在时,主要看import导入方式:import xxx from ""就导入export default;import {xxx} from ""就导入多个export导出属性。一般来说,尽量不要混在一起用,以防搞混。

    // test1.js
    export const a = 1;
    const b = 2;
    export default b;
    
    // test2.js
    import a from "./test1"
    console.log(a);// 2
    // import { a } from "./test1"
    // console.log(a); 1
    

require和import

  • require运行时加载,导入的是exports(exports|module.exports)导出的内容。

    // test1.js
    let a = 2;
    module.exports = a;
    
    // test2.js
    const a = require('./test1');
    console.log(a);// 2
    
  • import编译时加载,由于是编译时加载,所以import命令会提升到整个模块的头部。导入的是export(export|export default)导出的内容。

    // import 导入的用法
    import fs from 'fs'
    import {readFile} from 'fs' //从 fs 导入 readFile 模块
    import {default as fs} from 'fs' //从 fs 中导入使用 export default 导出的模块
    import * as fileSystem from 'fs' //从 fs 导入所有模块,引用对象名为 fileSystem
    import {readFile as read} from 'fs' //从 fs 导入 readFile 模块,引用对象名为 read
    
    // import(modulePath) 
    import('').then()//动态导入模块 
    // 表达式加载模块并返回一个 promise,该 promise resolve 为一个包含其所有导出的模块对象。
    

require/exports和import/export区别?

  1. 出处不同:

    类型年份出处
    require/exports2009CommonJS
    import/export2015ECMAScript2015(ES6)
  2. 兼容性不同:

    环境require/exportsimport/export
    Node.js所有版本Node 9.0+
    Chrome不支持61+
    Firefox不支持60+
    Safari不支持10.1+
    Edge不支持16+

注意地,import/export在nodejs中使用时,Node 9.0+启动需加上 flag --experimental-modules) Node 13.2+(直接启动)

  • 原生浏览器不支持 require/exports,可使用支持 CommonJS 模块规范的 Browsersify、webpack 等打包工具,它们会将 require/exports 转换成能在浏览器使用的代码。
  • import/export 在浏览器中无法直接使用,我们需要在引入模块的script元素上添加type="module" 属性。
  • 即使 Node.js 13.2+ 可以通过修改文件后缀为 .mjs 来支持 ES6 模块 import/export,但是Node.js 官方不建议在正式环境使用。目前可以使用 babel 将 ES6 的模块系统编译成 CommonJS 规范(注意:语法一样,但具体实现还是 require/exports)
  1. require/exports 是运行时动态加载,import/export 是静态编译。

    CommonJS 加载的是一个对象(即 module.exports 属性),该对象只有在脚本运行完才会生成。而ES6 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。- 阮一峰

  2. 导入导出用法不同。

  3. 是否采用严格模式。import/export 导出的模块默认调用严格模式,require/exports 默认不使用严格模式,可以自定义是否使用严格模式。

  4. 引用位置限制。ES6 模块可以在 import 引用语句前使用模块,CommonJS 则需要先引用后使用

  5. 使用位置限制。import/export 只能在模块顶层使用,不能在函数、判断语句等代码块之中引用;require/exports 可以。

参考资料

# require和import的区别