在讨论require和import的区别之前,我们首先要了解的是,es6module的代码最终都会被打包工具转化为require才能实行,关于这俩的区别网上有大把的文章,但是都不是很系统,小编这里整合了一下,总结了一下这两者区别,require遵循的是commonjs规范,import遵循的是es6module规则。
区别1:require的过程是赋值过程,通过require引入基础数据类型时,属于复制该变量。通过require引入复杂数据类型时,数据浅拷贝该对象。
基本类型导入示例代码如下:
// a.js
let count = 1
let setCount = () =>{
count++
}
setTimeout(() =>{
console.log('a', count)
}, 1000)
module.exports = {
count,
setCount
}
// b.js
let obj = require('./a.js')
obj.setCount()
console.log('b', obj.count)
//node b.js
//b 1
//a 2
//可以看出,count在b.js文件中复制了一份,
//setCount只改变了a.js中count值
关于对象的导入示例代码如下:
// a.js
let obj = {
count: 1
}
let setCount = () =>{
obj.count++
}
setTimeout(() =>{
console.log('a', obj.count)
}, 1000)
module.exports = {
obj,
setCount
}
// b.js
let data = require('./a.js')
data.setCount()
console.log('b', data.obj.count)
//node b.js
//b 2
//a 2
//从以上可以看出,a.js和b.js实际上指向同一个obj对象
在b.js这种引入a.js导入对象obj,a.js在0.5秒后改变对象obj的count属性,b.j一秒后输出obj.count,obj.count也改变了,所以说明两个文件中的对象obj指向的是同一个内存地址。
import的导入过程是解构过程,并且是强绑定的。
1、不管是基础(复杂)数据类型,都只是对该变量的动态只读引用。
2、动态在于一个模块中变量的变化会影响到另一个模块;只读在于从某个模块引入一个变量时,不允许修改该变量的值。对于复杂数据类型,可以添加属性和方法,但是不允许指向另一个内存空间。
这里要强调一点用import导入的数据被变量接收后,这些变量类似用const定义过,都是只读的,不允许重新赋值,引用类型可以增删修改属性。
代码如下:
//a.js
let a = 100;
export {a};
//b.js
import {a} from "./a.js";
a = 200;
上面的代码会报错;
从上面的区别中可以看出,不论是require还是import导入的是引用类型的话,只要不对接收的变量重新赋值,使用方式是一样的,引用类型数据指向的是同一个内存地址。
但是基本类型的使用就不同了,使用require导入的基本类型等于是拷贝了一个新的值,而import因为是动态引用所以被引入文件中的基本类型数据发生变化,引入的文件中的数据也会发生变化,我们将第一份代码改成import的方式,代码如下:
//a.js
let count = 1
function setcount(){
count ++
}
setTimeout(() => {
console.log('a', count)
}, 1000)
export {
count,
setcount
}
//b.js
import {count,setcount} from "./m1.js";
setcount();
console.log(count);
// node b.js
// b 2
// a 2
通过执行结果我们可以看到es6module引用基本类型是动态引用的,被引入文件的数据发生变化,会影响引入文件。
区别2、require使用的位置比较随意,比方说可以在函数内部使用,而import只能在文件作用域最外层使用。否则会报错:
即使用在if判断语句中也会出错:
这点require就比较灵活了。
区别3,import最终会被转化为require,这里面需要注意的是:Imports are hoisted 这句话暂时没有找到对应的中文语句来翻译,啥意思呢?就是import在编译转化为require时,会被挪到文件的顶部执行,代码如下:
// a.js
global.log = (str) => console.log(str);
import './b';
// b.js
global.log('hello world');
由于 import 被提升最终转化为的代码如下:
'use strict';
global.log('hello world');
global.log = function (str) {
return console.log(str);
};
所以会得到: log undefined。
关于 imports are hostied的解释在stackoverfllow于这样的解释:
Imports ARE hoisted! according to the spec of ModuleDeclarationInstantiation ALL the dependent Modules will be loaded before running any code. https://www.javascriptcn.com/read-29657.html
区别4,对于循环引用的处理:
require循环加载时,属于加载时执行。即脚本代码在require的时候,就会全部执行。一旦出现某个模块被"循环加载",就只输出已经执行的部分,还未执行的部分不会输出。
import循环加载时,ES6模块是动态引用。只要两个模块之间存在某个引用,代码就能够执行,也就是可能会陷入死循环。
代码如下:
// b.js
import {foo} from './a.js';
export function bar() {
console.log('bar');
foo();
}
// a.js
import {bar} from './b.js';
export function foo() {
console.log('foo');
bar();
console.log('执行完毕');
}
foo();
上面代码就会陷入死循环,执行结果如下:
改成commonjs规范来写如下:
//b.js
var foo = require("./a.js");
function bar() {
console.log('bar');
foo();
}
module.exports= bar
//a.js
var bar = require("./b.js");
function foo() {
console.log('foo');
bar();
console.log('执行完毕');
}
module.exports = foo()
foo();
以上代码,不会陷入死循环,但是会导致a.js提前导出,造成如下结果:
区别5,require是一个函数,在使用时传入的参数可以动态计算,例如:
require(“/b”+"/a.js")这样使用不会报错,但是如果使用 import “/b”+"/a.js",就会出现问题。
以上便是require和imports的区别,可能总结的还不是很全面,如果你有什么问题或者建议,欢迎留言。
引用资料:
https://www.zhihu.com/question/56820346
http://www.sohu.com/a/139735984_495695
http://www.cnblogs.com/unclekeith/p/7679503.html