import和require区别

先总结一下区别,之后再细聊:

  1. require是动态编译的,import是静态编译的。所以require后面的地址可以通过计算获取,import不能。
  2. require不会进行变量提升,import会。
  3. require导出的是一个拷贝值,import导入的是一个引用值(每个属性定义了getter)。
  4. require得到的变量可以修改,import得到的变量无法修改(因为es6里对于导出数据做了只读限制)

一. import 得到的变量是只读的,require得到的变量可以修改。

(1) Import

1
2
3
4
5
6
7
8
// a.js
const a = 1;
setTimeout(() => {
console.log('在a文件里', a);
}, 1000);
export {
a
}
1
2
3
4
5
// test.js
import { a } from './a';
console.log('a======111', a);
a = 3;
console.log('a=====222', a);

运行之后会得到:“a” is read-only

(2) require

1
2
3
4
5
// test.js
let a = require('./a');
console.log('a======', a);
a = 3;
console.log('a', a);

运行之后得到:

1
2
3
a====== { a: 1 }
a 3
在a文件里 1

只修改了test.js里面的数据

二. import 会进行提升,Import are hoisted。Require不会。

比如这个例子:

1
2
3
4
5
6
// a.js
global.log = (str) => console.log(str);
import './b';

// b.js
global.log('hello world');

由于 import 被提升所以这个例子会报 log undefined,用 babel 转义一下会得到:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// a.js

'use strict';

require('./b');

global.log = function (str) {

return console.log(str);

};

// b.js

'use strict';

global.log('hello world');

三. import静态编译,import的地址不能通过计算。require就可以。

例如 const url = “a” + “b”;

Import url 直接报错了

require(url)不会报错

所以require都会用在动态加载的时候。

四. require 输出的是一个值的拷贝, export 模块输出的是值的引用

对于require:如果导出的是基本类型,则得到的是一个值; 如果是对象,则得到的是一个对象地址的引用,总之就是拷贝了这个导出的值。

对于export:给module.exports上的每一个属性比如’name’,定义了一个属性描述对象Object.defineProperty(exports, ‘name’, { enumerable: true, get: getter },属性描述对象有一个getter,所以每次获取这个属性的时候其实都执行了函数getter,而不是像require对应的输出一样每次使用的都是缓存值

参考资料

1️⃣rquire会对引入进来的数据进行缓存

在test.js里面require(‘’./a’)之后再修改a则只会修改a文件内的数据,(如果a是导出一个对象,则都会修改,因为是地址引用,test和a所指向的是一个内存。如果a导出的是基本数据类型,则test里面拥有的是一个副本,也就是前面说的对数据做缓存)

2️⃣import之后对数据做修改会同时影响test和a里面的数据

注意不能 import a from ‘a’; a = 1;

上面已经说了import 是只读的。

但是 import a from ‘a’; a.theProp = 1; 是可以的,因为这并没有改a的引用地址,只是加一个属性

五. 多次引用同一个文件,require和import 对于 都是只执行一次

在这个方面import 和require都是一样的,引用相同文件都是只执行一次。

注意:在查看了webpack打包结果之后发现

其实一个文件就是对应一个module对象,module对象里面有个属性是module.exports.

每个文件在最终都会变成一个函数的执行体,这个函数有三个入参:

module, module.exports, webpack_require

导入和导出方式

(1)在commonjs(也就是上面说的require)模块里,有两种导出方式:

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

  2. exports.a = 1; exports.b = 2;

    这两种是完全等价的。之所以不能写成exports = {a:1, b:2}是因为exports作为形参传进来之后,如果执行的是exports = {a:1, b:2z}这样的赋值语句,是不会改变传入的实参的。而我们的目的是改变传入的实参,所以需要写成上面的两种方式之一,这两种方式都是可以改变实参的。

require的时候可以直接拿到module.exports的值,然后再分别取出里面的属性。

(2)在ES(也就是上面说的import)模块里,有很多种导出方式:

1
const a = {a1: 1; a2: 2};
  1. export { a }; // 在module.exports 上定义一个a属性,属性值是a;

    import { a } ;

  2. exports const a = a;// 在module.exports 上定义一个a属性,属性值是a;

    Import { a };

  3. exports default a; // 在module.exports 上定义一个default属性, 属性值是a;

    Import a;

对上面3种情况进行解释: import 的时候,如果不是对象结构赋值的话(也就是{ a }),会默认是import default,就像第3种情况,import a 会解析成import default as a,其实就是取出了module.exports的default属性。

现在假如这么写

1
2
// a.js
export { a };
1
2
3
4
5
6
7
8
9
// index.js
import a from './a'; // a 得到的值是undefined。因为这句话会被解析成
// import default as a from './a';
// 而我们在导出的时候是没有定义default的,只定义了a属性,所以会返回undefined.
// 在ES模块中,我们是没有办法获取到整个module.exports对象的,因为只要你写的不是对象解构赋值的形式,
// webpack 就会解析成default形式,所以要拿到整个module.exports对象,需要这样写:
// import * as Req from './ajax.js'; 这样取得的Req就是整个module导出的对象。【他是一个Module类的
// 实例】
// 而在commonjs模块里面直接require('./ajax.js')能拿到module.exports对象,【他是没经过处理的原对象】