一文带你搞懂JS中导入模块import和require的区别
作者:Choicc 发布时间:2023-07-21 03:24:18
js中用import导入模块和用require导入模块的区别
JavaScript中,模块是一种可重用的代码块,它将一些代码打包成一个单独的单元,并且可以在其他代码中进行导入和使用。在导入模块时,JavaScript中有两种常用的方式:使用import
和require
。
import
是ES6引入的新特性,它允许你以声明式的方式导入其他模块中的内容。require
是Node.js中的特性,它允许你使用一个函数来加载和导入其他模块。
下面是两种导入模块的方式的详细比较:
导入方式
import
导入的方式是使用关键字import
加上大括号(如果需要导入的内容是命名导出的话),再加上模块名的方式进行导入。例如:
import { func1, func2 } from './myModule';
require
导入的方式是使用require
函数,将需要导入的模块路径作为参数传递给该函数。例如:
const myModule = require('./myModule');
文件类型
import
只能导入ES6模块或者使用Babel等工具转化为ES6模块的代码。而require
则可以导入CommonJS模块、AMD模块、UMD模块以及Node.js内置模块等多种类型的模块。
变量提升
在ES6中,import
语句是静态执行的,意味着它们在模块内部的顶层执行,并且在模块内部创建一个局部作用域。这意味着导入的变量只在模块内部可见,并且不会影响模块外部的变量。因此,使用import
导入的变量是不会被提升的。
require
函数是动态执行的,这意味着它在运行时执行,并且不会在模块内部创建一个局部作用域。因此,使用require
导入的变量是可以被提升的。
ps:如何理解import语句是静态执行的和require函数是动态执行的?
理解import
语句是静态执行的和require
函数是动态执行的,需要先了解这两个概念的含义。
静态执行是指在编译阶段就能够确定其执行结果的代码执行方式。在JavaScript中,import
语句属于静态执行的代码,也就是说,当JavaScript引擎执行代码时,会在编译阶段对import
语句进行静态分析,确定所导入的模块,并在运行时加载这些模块。
动态执行是指在运行时才能确定其执行结果的代码执行方式。在JavaScript中,require
函数属于动态执行的代码,也就是说,当JavaScript引擎执行代码时,会在运行时动态地确定所需的模块,并加载这些模块。
因此,可以理解为:
import
语句是静态执行的,因为在编译阶段就能够确定所导入的模块,从而在运行时快速加载这些模块。require
函数是动态执行的,因为在运行时才能够确定所需的模块,需要动态地加载这些模块。
值得注意的是,由于import
语句是静态执行的,因此在代码中不能使用变量或表达式作为模块路径,而只能使用字符串字面量。而require
函数则可以接受变量或表达式作为模块路径,从而动态地确定所需的模块。
导出方式
import
和require
在导出方式上也有一些区别。import
使用ES6的导出方式,可以使用命名导出和默认导出两种方式进行导出。例如:
// 命名导出
export function func1() {}
// 默认导出
export default {}
而require
使用CommonJS的导出方式,只能使用默认导出方式进行导出。例如:
// 默认导出
module.exports = {};
require
除了可以使用 module.exports
导出模块,还可以使用 exports
对象。实际上,exports
对象是 module.exports
的一个引用。当使用 exports
导出时,实际上是向 module.exports
对象添加属性和方法。
模块作用域
在JavaScript中,每个模块都有自己的作用域,模块之间的变量是互相隔离的,不会相互干扰。这也是模块化编程的一个主要特点。
在使用import
导入模块时,实际上是在模块内部创建了一个指向被导入模块的引用,而不是直接复制模块中的变量。因此,当不同的文件中使用import
导入相同的模块时,它们实际上是共享了同一个模块实例,所以可以访问和修改同一个模块中的变量。
而在使用require
导入模块时,实际上是将导入模块中的变量直接复制到(可以理解为浅拷贝)当前模块的作用域中。因此,当不同的文件中使用require
导入相同的模块时,它们实际上是拥有各自独立的模块实例,彼此之间不会共享模块中的变量。
需要注意的是,如果使用require
导入的模块中含有可变状态的对象,那么在不同文件中修改该对象的变量会相互影响。这也是require
在某些情况下会产生一些难以预测的副作用的原因之一。而使用import
导入的模块,由于是共享同一个模块实例,相对来说更容易管理和控制。
如果使用require
导入的模块中含有可变状态的对象,比如一个对象的属性值可以被修改,那么当在不同的文件中修改这个对象中变量时,由于require
会将导入的模块中的变量直接复制到当前模块的作用域中(类似于浅拷贝,模块中的普通变量(例如字符串、数字等)是非共享的,而对象的变量则是能被修改共用的。),因此会导致这个对象的变量在不同文件中的值相互影响。
举个例子,假设有一个config.js
模块,其中定义了一个可变的对象config
,并且在main.js
和app.js
两个文件中使用了require
导入该模块:
config.js:
// config.js
let config = {
env: 'dev',
port: 3000
}
module.exports = config;
main.js:
// main.js
const config = require('./config.js');
// 修改config对象的属性值
config.port = 4000;
console.log(`config.port in main.js: ${config.port}`);
app.js:
// app.js
const config = require('./config.js');
console.log(`config.port in app.js: ${config.port}`);
当执行main.js
和app.js
时,它们都会通过require
导入config.js
模块,并且main.js
中修改了config
对象的port
属性值。那么当app.js
输出config.port
时,它实际上输出的是被main.js
修改后的port
属性值,而不是config.js
模块原本定义的值。这就是因为require
导入的模块中含有可变状态的对象或变量,在不同文件中修改该对象或变量会相互影响的原因。
需要注意的是,这种影响是由于模块之间共享同一个对象的引用造成的,而不是模块本身的问题。因此,在编写模块时,需要谨慎地处理可变状态的对象,尽量避免在不同模块之间共享同一个对象的引用,以避免出现不可预测的副作用。
最后
为了避免使用require
导入的模块中含有可变状态的对象或变量,在不同文件中修改该对象或变量会相互影响的副作用,有以下几种方法:
1.使用import
代替require
:使用import
导入模块时,不同文件导入同一个模块实际上是共享了同一个模块实例,因此可以避免使用require
时出现的副作用。
2.使用纯函数:纯函数是指输入相同的参数,输出结果也相同,并且不会对外部环境产生任何副作用的函数。如果在模块中使用纯函数,那么即使该模块中的变量被修改了,但由于纯函数不会产生副作用,因此在不同文件中调用该函数时,输出结果也不会发生变化。
3.使用常量或不可变对象:常量或不可变对象指的是一旦定义后就无法再被修改的值。如果在模块中使用常量或不可变对象,那么即使该模块中的变量被修改了,但由于常量或不可变对象无法被修改,因此在不同文件中调用该变量时,其值也不会发生变化。
4.使用模块的副本:可以通过在模块导出时返回一个副本,而不是直接返回模块内部的对象或变量。这样在不同文件中使用require
导入该模块时,它们会得到不同的副本,而不是共享同一个模块实例。例如:
// config.js
let config = {
env: 'dev',
port: 3000
}
module.exports = Object.assign({}, config);
在导出时使用Object.assign
方法返回config
对象的一个副本,从而避免了不同文件之间共享同一个模块实例的副作用。
需要注意的是,这些方法并不是万无一失的,而是根据具体情况选用合适的方法来避免副作用。在编写模块时,需要考虑模块中的变量是否需要共享,是否可以被修改,以及模块在不同文件中被调用时可能产生的副作用等因素,从而选择合适的方法来避免副作用。
来源:https://juejin.cn/post/7205487413350498360


猜你喜欢
- python 与 C++ dlib人脸检测结果对比,供大家参考,具体内容如下说明:由于项目需求发现Linux下c++使用dlib进行人脸检测
- Vue 的用法没有变化:<template lang="pug"> transition(name=&quo
- 1、Golang指针在介绍Golang指针隐式间接引用前,先简单说下Go 语言的指针 (Pointer),一个指针可以指向任何一个值的内存地
- 之前想爬取一些淘宝的数据,后来发现需要登录,找了很多的资料,有个使用request的sessions加上cookie来登录的,cookie的
- 同志们,经过不懈的努力,查了各种文档,终于鼓捣出了一个稍微像样一点的node项目,当然如果直接拿去项目里用,这个demo还太简单,毕竟一个完
- 看了好多登录注册和注销的教程,很乱,很迷,然后总结了一下,简单的做了一个登录,注册和注销的页面。1,首先,使用pycharm创建一个项目单击
- 本文实例讲述了python通过apply使用元祖和列表调用函数的方法。分享给大家供大家参考。具体实现方法如下:def my_fuc(a, b
- “选项卡”经常会被混同为“滑动门”,滑动门只是一种背景自适应内容的实现方法,不只可以应用在导航菜单,一样也可以应用在其他地方。例如选项卡中就
- 目录plsql141. 安装注册使用 1.激活了会提示激活成功plsql developer14是由Allround Automa
- 一般来说,使用线程有两种模式, 一种是创建线程要执行的函数, 把这个函数传递进Thread对象里,让它来执行. 另一种是直接从Thread继
- 一. 数据的格式首先我们需要x,y,z三个数据进行画图。从本实验用到的数据集KITTI 00.txt中举例:1.000000e+00 9.0
- 一、Background当想将照片序列合成延时摄影视频时,可能会发现照片中缺少一张,或者照片序列是跨时间、并不连续的,如图1所示,但PR中只
- 本文为大家分享了Python2.7与Python3.6环境切换的具体方法,供大家参考,具体内容如下系统支持为:Ubuntu18.04系统默认
- 什么是pdb不知道大家在用Python写代码出现报错时是怎样调试的,从报错提示定位回去一步一步check每一行?如果没有IDE或者命令行写代
- <html><head><title>过滤空格</title><SCRIPT LANG
- 免责声明:本教程所有资源均来源于网络;仅用于学习交流,请勿用于任何商业行为;如需要,请使用正版授权;侵权联删。idea不同版本激活方法,ID
- 在训练循环中,定期调用 saver.save() 方法,向文件夹中写入包含了当前模型中所有可训练变量的 checkpoint 文件。save
- 如下所示:data = np.random.randn(20)factor = pd.cut(data,4)pd.get_dummies(f
- 网页中使用flash可以增强页面的动态交互效果,特别是用flash来制作广告,效果更好。经常使用flash的人,可能就碰到了flash会遮住
- 用比较笨的方法来做abc ="AlkjA;lkjlkjAlkAkjAlkjAAAA" if instr(abc,&quo