1.1.export、exports、modules.exports 和 require 、import 的一些组合套路和坑:
引入: require / import
导出:export / module.exports / exports
require 用来加载代码,而 exports 和 module.exports 则用来导出代码
1.1.1.nodejs中exports 和 module.exports 的区别
1.module.exports 初始值为一个空对象 {}
2.exports 是指向的 module.exports 的引用
3.require() 返回的是 module.exports 而不是 exports
实例解释上述区别:
test.js:
var a = {name: 1};
var b = a;
console.log(a);
console.log(b);
b.name= 2;
console.log(a);
console.log(b);
var b = {name: 3};
console.log(a);
console.log(b);
运行 test.js 结果为:
{ name: 1 }
{ name: 1 }
{ name: 2 }
{ name: 2 }
{ name: 2 }
{ name: 3 }
解释:
a 是一个对象,b 是对 a 的引用,即 a 和 b 指向同一块内存,所以前两个输出一样。
当对 b 作修改时,即 a 和 b 指向同一块内存地址的内容发生了改变,所以 a 也会体现出来,所以第三四个输出一样。
当 b 被覆盖时,b 指向了一块新的内存,a 还是指向原来的内存,所以最后两个输出不一样。
综上所述:
其实,Module.exports才是真正的接口,exports只不过是它的一个辅助工具。最终返回给调用的是Module.exports而不是exports。
所有的exports设置的属性和方法,都赋值给了Module.exports。当然前提就是Module.exports本身不具备任何属性和方法。如果Module.exports已经具备一些属性和方法,那么exports收集来的信息将被忽略。
如果你没有显式的给Module.exports设置任何属性和方法,那么你的模块就是exports设置给Module.exports的属性。
Module.exports模块可以是任何合法的javascript对象:boolean, number, date, JSON, string, function, array等等。
如果你想你的模块是一个特定的类型就用Module.exports。如果你想的模块是一个典型的“实例化对象”就用exports。推荐使用exports导出,除非你打算从原来的“实例化对象”改变成一个类型。
1.1.2.ES6:export default 和 export 区别
1.在一个文件或模块中,export、import可以有多个,export default仅有一个。
2.通过export方式导出,在导入时只能以真实的函数名或者类名导出,且要加{ };export default方式导出,在import导入时],可以给模块起任何变量名,且不需要用大括号包含。
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'; //导入的时候没有花括号
3.重命名
//a.js
let sex = "boy";
export default sex(sex不能加大括号)
// b.js
import any from "./a.js"
import any12 from "./a.js"
console.log(any,any12) // boy,boy
1.1.3.import与require区别
Nodejs 不支持 import 和 export,导入用require, 导出用module.exports (ps: 不知从什么时候开始,es6居然已经支持module.exports了。)
import与export是es6的语法,他们打包的时候都会经过babel转码成require、module.exports
尽管es6兼容以上所有,但需要注意:
在webpack打包的时候,可以在js文件中混用 require 和 export。但是不能混用 import 以及 module.exports所以统一改成 ES6 的方式编写: import 和 export
1.1.4.es6 : import { ... } from '...' 实例
lib.js:
//
export const a = () => 123
export const b = () => 456
export const c = () => 789
__________________________________________________________
//
module.exports = {
a: () => 123,
b: () => 456,
c: () => 789,
}
__________________________________________________________
// export 对象导出
//请注意,这里的 { a, b, c } 并不是es6 对 key: value 形式的缩写
//而是只能以这种方式写
const a = () => 123
const b = () => 456
const c = () => 789
export { a, b, c }
__________________________________________________________
main.js:
import { a, b, c } from './lib.js'
console.log(a()) // => 123
console.log(b()) // => 345
console.log(c()) // => 678
1.2.es6 模块
1.2.1.模块概要
将一个大程序拆分成依赖的小文件,再拼装起来。其他语言都有这项功能,比如 Ruby 的require、Python 的import,甚至 CSS 都有@import。
在 ES6 之前,社区制定了一些模块加载方案,最主要的有 CommonJS 和 AMD 两种。前者用于服务器,后者用于浏览器。
ES6 在语言标准的层面上,实现了模块功能,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块方案。
ES6模块的设计思想,是尽量的静态化,使得编译时就能确定模块的依赖关系(编译时加载),以及输入和输出的变量。CommonJS和AMD模块,都只能在运行时确定这些东西(运行时加载)。
不是说js不需要编译吗,那‘ES6模块是编译时加载’是什么意思
JS的“编译”可能指三种流程:
transform:把ES6+的JS转译成es5或以下引擎能运行的代码。
bundle:合并依赖的模块。
uglify:压缩丑化,减小体积。
ES6模块是编译时加载:模块之间的依赖关系,在运行之前(即编译时)就通过静态分析来确定好了的。所以有时候说编译时可以做“静态优化”,运行时加载因为只有运行时才能得到这个对象,导致完全没办法做“静态优化”。
CommonJS 和 AMD 模块与es6 模块区别
CommonJS 模块运行时加载。let _fs = require('fs');
整体加载CommonJS 模块(即加载模块的所有方法),生成一个对象(_fs),然后再从这个对象上面读取 3 个方法。即CommonJS 模块就是对象。
ES6 可以在编译时就完成模块加载,效率要比 CommonJS 模块的加载方式高。import { stat, exists, readFile } from 'fs';
ES6 模块不是对象,而是通过export命令显式指定输出的代码,再通过import命令输入。还有成为浏览器和服务器通用的模块解决方案(将来)。
ES6 的模块自动采用严格模式,不管你有没有在模块头部加上"use strict";。
-变量必须声明后再使用
-禁止this指向全局对象
尤其需要注意this的限制。ES6 模块之中,顶层的this指向undefined,即不应该在顶层代码使用this。
1.2.2.export和import
模块功能主要由两个命令构成:export和import。
export命令用于输出模块的对外接口,import命令用于输入其他模块来提供的功能。
export命令规定的是对外的接口格式:
// 报错
var m = 1;
export m;
// 写法一
export var m = 1;
// 写法二
var m = 1;
export {m};
// 写法三
var n = 1;
export {n as m};
// 报错
function f() {}
export f;
// 正确
export function f() {};
// 正确
function f() {}
export {f};
最后,export命令可以出现在模块的任何位置,只要处于模块顶层就可以。如果处于块级作用域内,就会报错,import命令也是如此。export语句放在函数之中,条件代码块之中,结果报错。
import命令接受一对大括号,里面指定从其他模块导入的变量名。大括号里面的变量名,必须与被导入模块对外接口的名称相同。import { lastName as surname } from './profile.js';
如果想为输入的变量重新取一个名字,import命令要使用as关键字,将输入的变量重命名。
import命令输入的变量都是只读的,因为它的本质是输入接口。也就是说,不允许在加载模块的脚本里面,改写接口。
import {a} from './xxx.js'
a = {}; // Syntax Error : 'a' is read-only;
但是,如果a是一个对象,改写a的属性是允许的。a的属性可以成功改写,并且其他模块也可以读到改写后的值。但一般都当作完全只读,轻易不要改变它的属性。
import {a} from './xxx.js'
a.foo = 'hello'; // 合法操作
import命令具有提升效果,会提升到整个模块的头部,首先执行。
foo();
import { foo } from 'my_module';
//import命令是编译阶段执行的,在代码运行之前。
由于import是静态执行,所以不能使用表达式和变量
// 报错
import { 'f' + 'oo' } from 'my_module';
// 报错
let module = 'my_module';
import { foo } from module;
// 报错
if (x === 1) {
import { foo } from 'module1';
} else {
import { foo } from 'module2';
}
import语句会执行所加载的模块
如果多次重复执行同一句import语句,那么只会执行一次
import { foo } from 'my_module';
import { bar } from 'my_module';
1.2.3.export default 命令
export default命令,为模块指定默认输出。其他模块加载该模块时,import命令可以为该匿名函数指定任意名字。需要注意的是,这时import命令后面,不使用大括号。
export default function foo() {
console.log('foo');
}
// 或者写成
function foo() {
console.log('foo');
}
export default foo;
上面代码中,foo函数的函数名foo,在模块外部是无效的。加载的时候,视同匿名函数加载。
export与export default区别
// 第一组
export default function crc32() {
}
import crc32 from 'crc32';
// 第二组
export function crc32() {
};
import {crc32} from 'crc32';
export default命令只能使用一次,一个模块只能有一个默认输出,所以,import命令后面才不用加大括号。本质上,export default就是输出一个叫做default的变量或方法,然后系统允许你为它取任意名字。
function add(x, y) {
return x * y;
}
export {add as default};
import { default as foo } from 'modules';
正是因为export default命令其实只是输出一个叫做default的变量,所以它后面不能跟变量声明语句。
// 正确
export var a = 1;
// 正确
var a = 1;
export default a;
// 错误
export default var a = 1;
//export default a的含义是将变量a的值赋给变量default。
// 正确
export default 42;
// 报错
export 42;
如果想在一条import语句中,同时输入默认方法和其他接口
import _, { each, each as forEach } from 'lodash';
export default function (obj) {
}
export function each(obj, iterator, context) {
}
export { each as forEach };
export default也可以用来输出类。
// MyClass.js
export default class { ... }
// main.js
import MyClass from 'MyClass';
let o = new MyClass();
1.2.4.export 与 import 的复合写法
如果在一个模块之中,先输入后输出同一个模块,import语句可以与export语句写在一起。
export { foo, bar } from 'my_module';
// 可以简单理解为
import { foo, bar } from 'my_module';
export { foo, bar };
foo和bar实际上并没有被导入当前模块,只是相当于对外转发了这两个接口,当前模块不能直接使用foo和bar。
// 接口改名
export { foo as myFoo } from 'my_module';
// 整体输出
export * from 'my_module';
//默认接口
export { default } from 'foo';
1.2.5.模块的继承
// circleplus.js
export * from 'circle';
export var e = 2.71828182846;
export default function(x) {
return Math.exp(x);
}
// main.js
import * as math from 'circleplus';
import exp from 'circleplus';
console.log(exp(math.e));
export * 命令会忽略circle模块的default方法。
import exp表示,将circleplus模块的默认方法加载为exp方法。
1.2.6.常量跨模块
const声明的常量只在当前代码块有效。
一个常量也可以被多个模块共享。如:
// constants.js 模块
export const A = 1;
export const B = 3;
export const C = 4;
// test1.js 模块
import * as constants from './constants';
console.log(constants.A); // 1
console.log(constants.B); // 3
// test2.js 模块
import {A, B} from './constants';
console.log(A); // 1
console.log(B); // 3
如果要使用的常量非常多,可以建一个专门的constants目录,将各种常量写在不同的文件里面,保存在该目录下。
// constants/db.js
export const db = {
url: 'http://my.couchdbserver.local:5984',
admin_username: 'admin',
admin_password: 'admin password'
};
// constants/user.js
export const users = ['root', 'admin', 'staff', 'ceo', 'chief', 'moderator'];
然后,将这些文件输出的常量,合并在index.js里面。
// constants/index.js
export {db} from './db';
export {users} from './users';
使用的时候,直接加载index.js就可以了。
// script.js
import {db, users} from './index';
网友评论