浅析import与require的基本用法、引入方式区别以及在模块加载时间、模块本质、性能、严格模式、值与引用的5个方面区别、export导出模块接口
在 es6 之前 JS 一直没有自己的模块语法,为了解决这种尴尬就有了require.js等AMD或CMD方式的出现。在 es6 发布之后 JS 又引入了 import 的概念使得不清楚两者之间的区别的同学在实际使用过程中造成了自己的误解,在查阅了相关资料之后在此记录下自己的小小见解。
一、require 与 import 基本用法及引入方式区别
1、require的基本语法
核心概念:在导出的文件中定义 module.export,导出的对象的类型不予限定(可以是任何类型,字符串,变量,对象,方法),在引入的文件中调用 require() 方法引入对象即可。
//a.js中
module.export = {
a: function(){
console.log(666)
}
}
//b.js中
var obj = require('../a.js')
obj.a() //666
本质上是将要导出的对象赋值给module这个的对象的export属性,在其他文件中通过require这个方法访问该属性
require 的使用非常简单,它相当于module.exports的传送门,module.exports后面的内容是什么,require的结果就是什么,对象、数字、字符串、函数……再把require的结果赋值给某个变量,相当于把require和module.exports进行平行空间的位置重叠
require('./a')(); // a模块是一个函数,立即执行a模块函数
var data = require('./a').data; // a模块导出的是一个对象
var a = require('./a')[0]; // a模块导出的是一个数组
2、import的基本语法
核心概念:导出的对象必须与模块中的值一一对应,换一种说法就是导出的对象与整个模块进行解构赋值。抓住重点,解构赋值!
//a.js中
// 最常使用的方法,加入default关键字代表在import时可以使用任意变量名且不需要花括号{}
export default {
a: function(){
console.log(666)
}
}
export function(){ //导出函数
}
export {newA as a ,b,c} // 解构赋值语法(as关键字在这里表示将newA作为a的数据接口暴露给外部,外部不能直接访问a)
//b.js中
import a from '...' //import常用语法(需要export中带有default关键字)可以任意指定import的名称
import {...} from '...' // 基本方式,导入的对象需要与export对象进行解构赋值。
import a as biu from '...' //使用as关键字,表示将a代表biu引入(当变量名称有冲突时可以使用这种方式解决冲突)
import {a as biubiubiu,b,c} //as关键字的其他使用方法
二、require 与 import 主要区别
1、区别1:模块加载的时间
require:运行时加载 —— require 是赋值过程,且是运行时才执行
import:编译时加载(效率更高)—— import 是解构过程,且是编译时执行
由于是编译时加载,所以 import 命令会提升到整个模块的头部,下面代码不会报错,正常执行
test();
import { test} from '/test';
2、区别2:对性能的影响
require的性能相对于import稍低:因为 require 是在运行时才引入模块,并且还赋值给某个变量;
而 import 只需要依据 import 中的接口在编译时引入指定模块,所以性能稍高。
3、区别3:模块的本质
require:模块就是对象,输入时必须查找对象属性
import:ES6 模块不是对象,而是通过 export 命令显式指定输出的代码,再通过 import 命令输入(这也导致了没法引用 ES6 模块本身,因为它不是对象)。由于 ES6 模块是编译时加载,使得静态分析成为可能。有了它,就能进一步拓宽 JavaScript 的语法,比如引入宏(macro)和类型检验(type system)这些只能靠静态分析实现的功能。
// CommonJS模块
let { exists, readFile } = require('fs');
// 等同于
let fs = require('fs');
let exists = fs.exists;
let readfile = fs.readfile;
上面CommonJs模块中,实质上整体加载了fs对象(fs模块),然后再从fs对象上读取方法
// ES6模块
import { exists, readFile } from 'fs';
上面ES6模块,实质上从fs模块加载2个对应的方法,其他方法不加载
4、区别4:严格模式
CommonJs模块和ES6模块的区别:
(1)CommonJs模块默认采用非严格模式
(2)ES6 的模块自动采用严格模式,不管你有没有在模块头部加上 “use strict”;
(3)CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用,举例如下
// m1.js
export var foo = 'bar';
setTimeout(() => foo = 'baz', 500);
// m2.js
import {foo} from './m1.js';
console.log(foo); //bar
setTimeout(() => console.log(foo), 500); //baz
在 commom.js 中 module.export 之后导出的值就不能再变化,但是在 es6 的 export 中是可以的。
var a = 6
export default {a}
a = 7 //在es6中的export可以
var a = 6
module.export = a
a = 7 //在common.js中,这样是错误的
三、export导出模块接口
1、在要导出的接口前面,加入export指令。
// a.js
export default function() {}
export function a () {}
var b = 'xxx';
export {b}; // 这是ES6的写法,实际上就是{b:b}
setTimeout(() => b = 'ooo', 1000);
export var c = 100;
2、错误演示
// 错误演示
export 1; // 绝对不可以
var a = 100;
export a;
export 在导出接口的时候,必须与模块内部的变量具有一一对应的关系。直接导出1没有任何意义,也不可能在import的时候有一个变量与之对应。 export a
虽然看上去成立,但是 a
的值是一个数字,根本无法完成解构,因此必须写成 export {a}
的形式。即使a被赋值为一个function,也是不允许的。而且,大部分风格都建议,模块中最好在末尾用一个export导出所有的接口,例如:
export {fun as default,a,b,c};
四、export 和 export default 的区别
在JavaScript ES6中,export与export default均可用于导出常量、函数、文件、模块等,你可以在其它文件或模块中通过 import+(常量 | 函数 | 文件 | 模块)名 的方式,将其导入,以便能够对其进行使用,但在一个文件或模块中,export、import 可以有多个,export default仅有一个。
1、export
命令用于规定模块的对外接口。
一个模块就是一个独立的文件。该文件内部的所有变量,外部无法获取。
如果你希望外部能够读取模块内部的某个变量,就必须使用export
关键字输出该变量。下面是一个 JS 文件,里面使用export
命令输出变量。
export var firstName = 'aaa';
export var lastName = 'bbb';
export var year = 2021;
// 上面代码 ES6 将其视为一个模块,里面用export命令对外部输出了三个变量。
// export的写法,除了像上面这样,还有另外一种。
var firstName = 'aaa';
var lastName = 'bbb';
var year = 2021;
export {firstName, lastName, year};
上面代码在export
命令后面,使用大括号指定所要输出的一组变量。它与前一种写法(直接放置在var
语句前)是等价的,但是应该优先考虑使用这种写法。因为这样就可以在脚本尾部,一眼看清楚输出了哪些变量。
2、export与export default命令的区别:
import
命令接受一对大括号,里面指定要从其他模块导入的变量名。大括号里面的变量名,必须与被导入模块对外接口的名称相同。
如果想为输入的变量重新取一个名字,import
命令要使用as
关键字,将输入的变量重命名。
import { lastName as surname } from './profile.js';
3、export default
命令,为模块指定默认输出。
使用import
命令的时候,用户需要知道所要加载的变量名或函数名,否则无法加载。但是,用户肯定希望快速上手,未必愿意阅读文档,去了解模块有哪些属性和方法。为了给用户提供方便,让他们不用阅读文档就能加载模块,就要用到export default
命令,为模块指定默认输出。
4、与export命令的区别:
其他模块加载该模块时,import
命令可以为该匿名函数指定任意名字。
// export-default.js
export default function () {
console.log('foo');
}
// 上面代码是一个模块文件export-default.js,它的默认输出是一个函数。
// import-default.js 直接指定任意名字
import customName from './export-default';
customName(); // 'foo'
上面代码的import
命令,可以用任意名称指向export-default.js
输出的方法,这时就不需要知道原模块输出的函数名。需要注意的是,这时import
命令后面,不使用大括号。
本质上,export default
就是输出一个叫做default
的变量或方法,然后系统允许你为它取任意名字。所以,下面的写法是有效的。
// modules.js
function add(x, y) {
return x * y;
}
export {add as default};
// 等同于
// export default add;
// app.js
import { default as foo } from 'modules';
// 等同于
// import foo from 'modules';
正是因为export default
命令其实只是输出一个叫做default
的变量,所以它后面不能跟变量声明语句。
5、总结:
(1)export命令对外接口是有名称的,且import
命令从模块导入的变量名与被导入模块对外接口的名称相同,而export default命令对外输出的变量名可以是任意的,这时import
命令后面,不使用大括号。
(2)export default
命令用于指定模块的默认输出。显然,一个模块只能有一个默认输出,因此export default
命令只能使用一次。所以import命令后面才不用加大括号,因为只可能唯一对应export default
命令。