经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » JS/JS库/框架 » JavaScript » 查看文章
require()、import、import()有哪些区别?
来源:cnblogs  作者:路泽宇  时间:2023/11/15 9:22:29  对本文有异议

require()、import、import()是我们常用的引入模块的三种方式,代码中几乎处处用到。如果对它们存在模糊,就会在工作过程中不断产生困惑,更无法做到对它们的使用挥洒自如。今天我们来一起捋一下,它们之间有哪些区别?

 

一、前世今生

学一个东西,先弄清楚它为什么会出现、它的发展历史、它是做什么的,是很有必要的。看似与技术无关,却很有助于你对技术的理解。

require():

require()是CommonJS引入模块的函数。CommonJS是一种规范,主要用于nodejs,无法用于浏览器端。它是规范,而不是语法。本质上,它是在JS本身不支持模块的情况下,程序员发挥聪明才智,模拟出来的模块系统,在语言层面没有对JS做任何改动。它的原理实际上还是立即执行函数(IIFE)。

import:

JS在设计之初极为简单,没有模块系统,但开发中对模块化的需求却与日俱增。终于,ES6正式在语言标准的层面,引入了模块系统。import就是ES6的模块引入语句,作为ES6本身的语法,只要能写ES6的地方,都能使用它。它最大的特点,是静态引入,在编译时完成模块加载。这带来了加载效率高、静态分析等好处。

require()对比import,类似于VUE之于JS,对比ES6之于ES5的关系。

import():

相比require(),import好处多多,却也丢失了动态引入的优点,即在运行时根据实际需要选择引入模块。怎么办呢?

在ES2020中,引入了import()函数,它和require()一样是动态加载,不同的是,它是异步的,返回一个Promise对象,而require()是同步的。

 

二、缓存方式

(1)先来回答一个面试中常问的问题,一个模块多次引入,会执行几次?

require():

  1. // 2.js
  2. console.log('模块执行开始');
  3. let num = 1;
  4. module.exports = { num };
  5. console.log('模块执行结束');
  6. // testRequire.js
  7. let a = require('./2.js');
  8. let b = require('./2.js');
  9. console.log(typeof a);
  10. console.log(a === b);
  11. // 执行结果
  12. // 模块执行开始
  13. // 模块执行结束
  14. // object
  15. // true

import:

  1. // 1.js
  2. console.log('模块执行开始');
  3. export let num = 1;
  4. console.log('模块执行结束');
  5. // testImport.js
  6. import * as a from './1.js';
  7. import * as b from './1.js';
  8. console.log(typeof a);
  9. console.log(a === b);
  10. // 执行结果
  11. // 模块开始执行
  12. // 模块执行结束
  13. // object
  14. // true

import():

  1. // 1.js
  2. console.log('模块执行开始');
  3. export let num = 1;
  4. console.log('模块执行结束');
  5. // testImportFunction.js
  6. let a = await import('./1.js');
  7. let b = await import('./1.js');
  8. console.log(typeof a);
  9. console.log(a === b);
  10. // 执行结果
  11. // 模块开始执行
  12. // 模块执行结束
  13. // object
  14. // true

由此可见,它们三个,在代码中多次引入同一模块,模块都是只会执行一次。并且,输出结果完全相等(===),也就是指向同一个引用。

我们可以判断,它们都是第一次执行后把输出结果缓存了起来,多次引入不会再次执行,而是直接返回输出结果的引用。

(2)那么,它们对输出结果的缓存方式一样吗?

require():

  1. // 2.js
  2. let num = 1;
  3. let obj = {
  4. num: 1
  5. };
  6. function add() {
  7. num += 1;
  8. obj.num += 1;
  9. }
  10. module.exports = { num, obj, add };
  11. // testRequire.js
  12. let a = require('./2.js');
  13. console.log(a.num); // 1
  14. console.log(a.obj.num); // 1
  15. a.add();
  16. console.log(a.num); // 1
  17. console.log(a.obj.num); // 2

require的缓存方式,是对输出结果进行拷贝,而且是浅拷贝。值类型直接拷贝,引用类型拷贝内存地址。

import:

  1. // 1.js
  2. let num = 1;
  3. let obj = {
  4. num: 1
  5. };
  6. function add() {
  7. num += 1;
  8. obj.num += 1;
  9. }
  10. export { num, obj, add };
  11. // testImport.js
  12. import * as a from './1.js';
  13. console.log(a.num); // 1
  14. console.log(a.obj.num); // 1
  15. a.add();
  16. console.log(a.num); // 2
  17. console.log(a.obj.num); // 2

import并不对输出结果进行拷贝,而是直接指向输出结果的引用。

import():

  1. // 1.js
  2. let num = 1;
  3. let obj = {
  4. num: 1
  5. };
  6. function add() {
  7. num += 1;
  8. obj.num += 1;
  9. }
  10. export { num, obj, add };
  11. // testImportFunction.js
  12. let a = await import('./1.js');
  13. console.log(a.num); // 1
  14. console.log(a.obj.num); // 1
  15. a.add();
  16. console.log(a.num); // 2
  17. console.log(a.obj.num); // 2

import()也是一样的,直接指向输出结果的引用。

 

三、静态?动态?

静态引入:

所谓静态引入,就是在编译时引入,那么就不能使用在运行时才能得到结果的语法结构了,比如不能包在if语句里,引用路径不能使变量和表达式,要求必须是字符串字面量。

import就是静态引入。

动态引入:

动态引入,就是在运行时引入。因此可以根据条件判断来按需引入,引用路径也可以写成变量或表达式。

require()和import()都是动态引入。

 

四、同步?异步?

require():

  1. // 2.js
  2. console.log('模块执行开始');
  3. let num = 1;
  4. for (var i = 0; i < 1000000000; i++) {
  5. }
  6. module.exports = { num };
  7. console.log('模块执行结束');
  8. // testRequire.js
  9. let a = require('./2.js');
  10. console.log('执行其他代码');
  11. // 执行结果
  12. // 模块执行开始
  13. // 若干秒后...
  14. // 模块执行结束
  15. // 执行其他代码

require()引入模块,是同步的,但是因为是在服务端本地引用,同步引入完全没有问题。

import:

  1. // 1.js
  2. console.log('模块执行开始');
  3. let num = 1;
  4. await new Promise(resolve => {
  5. setTimeout(resolve, 3000);
  6. });
  7. export { num };
  8. console.log('模块执行结束');
  9. // testImport.js
  10. import * as a from './1.js';
  11. console.log('执行其他代码');
  12. // 执行结果
  13. // 模块执行开始
  14. // 3秒后...
  15. // 模块执行结束
  16. // 执行其他代码

这儿让我非常意外,也很困惑。都说import是异步引入的,为什么这儿的结果却显示它是同步的?哪位能解答我的疑惑?

import():

  1. // 1.js
  2. console.log('模块执行开始');
  3. let num = 1;
  4. await new Promise(resolve => {
  5. setTimeout(resolve, 3000);
  6. });
  7. export { num };
  8. console.log('模块执行结束');
  9. // testImportFunction.js
  10. import('./1.js');
  11. console.log('执行其他代码');
  12. // 执行结果
  13. // 执行其他代码
  14. // 模块执行开始
  15. // 3秒后...
  16. // 模块执行结束

没问题,import()是异步引入,返回的是一个Promise对象。

 

五、相互引用

require():

require()无法引入ES6模块,但可以使用import()函数来引入ES6模块。

import:

  1. // 2.js
  2. let num = 1;
  3. let obj = {
  4. num: 1
  5. };
  6. module.exports = { num, obj, add };
  7. // testImport.js
  8. import a from './2.js';
  9. console.log(a.num); // 1
  10. console.log(a.obj.num); // 1

import可以引入CommonJS模块,是把module.exports对象整体引入,类似于对exports default的接收,直接用一个变量来接收。

import():

  1. // 2.js
  2. let num = 1;
  3. let obj = {
  4. num: 1
  5. };
  6. module.exports = { num, obj, add };
  7. // testImportFunction.js
  8. let a = await import('./2.js');
  9. console.log(a.default.num); // 1
  10. console.log(a.default.obj.num); // 1

import()整体接收module.exports这个对象,并把它放在default属性下。

 

就总结这些吧。本人水平非常有限,写作主要是为了把自己学过的东西捋清楚。如有错误,还请指正,感激不尽。

原文链接:https://www.cnblogs.com/luzeyu/p/17832775.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号