您的位置:首页 > Web前端 > Node.js

【NodeJS】浅析 exports 与 module.exports 的区别 及 export default 与 export 的区别

2017-12-09 10:13 676 查看

模块的概念

  模块分为两类:原生模块和文件模块。原生模块即 Node.js API 提供的原生模块,原生模块在启动时已经被加载。文件模块为动态加载模块,加载文件模块的工作主要由原生模块 module 来实现和完成。原生模块在启动时已经被加载,而文件模块则需要通过调用 Node.js 的 require 方法来实现加载。

  需要了解的一点是,Node.js 会对原生模块和文件模块都进行缓存,因此在第二次 require 该模块时,不会有重复开销去加载模块,只需要从缓存中读取相应模块数据即可。

exports 与 module.exports 的区别

  exports 和 module.exports 的作用都是将文件模块的方法和属性暴露给 require 返回的对象进行调用。但是二者之间存在本质的区别。

1)exports 的属性和方法都可以被 module.exports 替代

如下面的代码,其作用都是一致的。

exports.name = 'bob' 和 module.exports.name = 'bob'


2)但 exports 不能替代 module.exports 方法

  可以理解为包含关系。所有的 exports 对象最终都是通过 module.exports 传递执行,因此可以更确切地说,exports 是给 module.exports 添加属性和方法。为了验证这一点,我们将文件中的 module.exports 对象打印出来与 exports 方法和属性进行对比,代码如下:

/* test.js */
exports.name = 'a';
exports.happy = function(console.log('happy'));
console.log(module.exports);
// 运行结果:{ name: 'a', happy: [Function] }


  从结果中可以看出,module.exports 相当于 require 返回的
4000
对象,也就是所有 require 返回的对象,实质上结果和 module.exports 是相同的。这里我们可以做个检测,常见一个index.js文件,代码如下:

/* index.js */
var obj = require('./test.js');
console.log(obj);
/* 运行结果:
{ name: 'a', happy: [Function] }
{ name: 'a', happy: [Function] }
*/


3)module.exports 方法还可以单独返回一个数据类型,而 exports 只能返回一个 object 对象。

当我们需要返回一个数组、字符串、数字等的类型时,就必须使用 module.exports。

总结:

  module.exports才是真正的接口,exports只不过是它的一个辅助工具。 最终返回给调用的是module.exports而不是exports。所有的exports收集到的属性和方法,都赋值给了Module.exports。当然,这有个前提,就是module.exports本身不具备任何属性和方法。如果,module.exports已经具备一些属性和方法,那么exports收集来的信息将被忽略。请看下例:

/* test.js */
exports.a = function(){
console.log('a')
}

module.exports = {a: 2}
exports.a = 1


/* index.js */
var obj = require('./test');
console.log(obj.a);
/* 运行结果:2 */


说明:exports在module.exports 被改变后,失效。

export default 命令

  使用import命令的时候,用户需要知道所要加载的变量名或函数名,否则无法加载。但是,用户肯定希望快速上手,未必愿意阅读文档,去了解模块有哪些属性和方法。为了给用户提供方便,让他们不用阅读文档就能加载模块,就要用到export default命令,为模块指定默认输出。

// export-default.js
export default function () {
console.log('foo');
}


上面代码是一个模块文件export-default.js,它的默认输出是一个函数。

其他模块加载该模块时,import命令可以为该匿名函数指定任意名字。

// import-default.js
import customName from './export-default';
customName(); // 'foo'


  上面代码的import命令,可以用任意名称指向export-default.js输出的方法,这时就不需要知道原模块输出的函数名。需要注意的是,这时import命令后面,不使用大括号。

export default命令用在非匿名函数前,也是可以的。

// export-default.js
export default function foo() {
console.log('foo');
}

// 或者写成

function foo() {
console.log('foo');
}

export default foo;


export default 与 export 区别

export default 与 export 区别:

1. export与export default均可用于导出常量、函数、文件、模块等

2. 你可以在其它文件或模块中通过import+(常量 | 函数 | 文件 | 模块)名的方式,将其导入,以便能够对其进行使用

3. 在一个文件或模块中,export、import可以有多个,export default仅有一个

4. 通过export方式导出,在导入时要加{ },export default则不需要

1.export
//a.js
export const str = "blablabla~";
export function log(sth) {
return sth;
}
对应的导入方式:

//b.js
import { str, log } from 'a'; //也可以分开写两次,导入的时候带花括号
---------------------------
2.export default
//a.js
const str = "blablabla~";
export default str;
对应的导入方式:

//b.js
import str from 'a'; //导入的时候没有花括号


使用export default命令,为模块指定默认输出,这样就不需要知道所要加载模块的变量名

以下引自 阮一峰的《ECMAScript 6 入门》

Module 概述

  历史上,JavaScript 一直没有模块(module)体系,无法将一个大程序拆分成互相依赖的小文件,再用简单的方法拼装起来。其他语言都有这项功能,比如 Ruby 的require、Python 的import,甚至就连 CSS 都有@import,但是 JavaScript 任何这方面的支持都没有,这对开发大型的、复杂的项目形成了巨大障碍。在 ES6 之前,社区制定了一些模块加载方案,最主要的有 CommonJS 和 AMD 两种。前者用于服务器,后者用于浏览器。

CommonJS规范

  这种模块加载机制被称为 CommonJS 规范。在这个规范下,每个 .js 文件都是一个模块,它们内部各自使用的变量名和函数名都互不冲突,例如,hello.js 和 main.js 都申明了全局变量 var s = ‘xxx’,但互不影响。

  

  一个模块想要对外暴露变量(函数也是变量),可以用module.exports = variable; ,一个模块要引用其他模块暴露的变量,用var ref = require(‘module_name’); 就拿到了引用模块的变量。

  ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。

  ES6 模块的设计思想,是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些东西。比如,CommonJS 模块就是对象,输入时必须查找对象属性。

// CommonJS模块
let { stat, exists, readFile } = require('fs');

// 等同于
let _fs = require('fs');
let stat = _fs.stat;
let exists = _fs.exists;
let readfile = _fs.readfile;


  上面代码的实质是整体加载fs模块(即加载fs的所有方法),生成一个对象(_fs),然后再从这个对象上面读取 3 个方法。这种加载称为“运行时加载”,因为只有运行时才能得到这个对象,导致完全没办法在编译时做“静态优化”。

ES6 模块不是对象,而是通过export命令显式指定输出的代码,再通过import命令输入。

// ES6模块
import { stat, exists, readFile } from 'fs';


  上面代码的实质是从fs模块加载 3 个方法,其他方法不加载。这种加载称为“编译时加载”或者静态加载,即 ES6 可以在编译时就完成模块加载,效率要比 CommonJS 模块的加载方式高。当然,这也导致了没法引用 ES6 模块本身,因为它不是对象。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: