Module 的语法的总结

10 篇文章 0 订阅

ES6中(目前最常使用)

1.导出(Export): 
  • 默认导出:一个模块只能默认导出一个变量、函数或类。可以使用 export default 关键字进行默认导出。
  • 命名导出:可以导出多个变量、函数或类。使用 export 关键字并指定名称进行命名导出。
  • // 默认导出
    export default myFunction;
    
    // 命名导出
    export const name = 'John';
    export function sayHello() {
      console.log('Hello!');
    }

 2.导入(Import):

  • 默认导入:导入默认导出的模块时,可以为其指定任意名称。
  • 命名导入:导入命名导出的模块时,需要使用相应的名称。
  • // 默认导入
    import myFunction from './myModule';
    
    // 命名导入
    import { name, sayHello } from './myModule';

 3.组合导入与导出: 

  • 导出时使用 export {name1, name2, ...} 将多个变量、函数或类一起导出。
  • 导入时使用 import * as alias from './myModule' 导入全部导出,并通过别名访问。
  • // 导出多个变量
    export { name1, name2 };
    
    // 导入全部导出,并使用别名访问
    import * as myModule from './myModule';
    console.log(myModule.name);

 4.动态导入: 

  • 使用 import() 函数可以在运行时动态加载模块。
  • import('./myModule')
      .then((module) => {
        module.myFunction();
      })
      .catch((error) => {
        console.error('Error:', error);
      });

 

模块的导入和导出的优点和缺点:

1. 默认导出(export default):

  •    - 优点: 可以为一个模块指定默认的导出内容,方便导入时不需要使用花括号。
  •    - 缺点: 每个模块只能有一个默认导出,限制了导出多个变量、函数或类的能力。

2. 命名导出(export):

  •    - 优点:可以导出多个变量、函数或类,并在导入时通过指定名称进行访问,提供了更灵活的导出方式。可以按需导出模块中特定的部分,减少不必要的资源消耗。

  •    - 缺点: 在导入时需要使用花括号指定导入的名称,稍微增加了代码的复杂性。

3. 导入全部(import * as alias):

  •    - 优点:可以将模块的全部导出内容作为一个对象导入,通过别名访问导出的变量、函数或类,方便集中管理。
  •    - 缺点:需要通过别名访问导出内容,会稍微增加代码的书写量。

4. 组合导出与导入(export/import { name1, name2, ... }):

  •    - 优点:可以将多个变量、函数或类一起导出或导入,提供了更灵活的方式来组织和管理代码。
  •    - 缺点: 需要使用花括号指定导出或导入的具体名称,可能会增加代码的复杂性。

5. 动态导入(import()):

  •    - 优点:可以在运行时根据条件或需要动态加载模块,提高应用程序的性能和可扩展性。

  •    - 缺点:在某些旧版浏览器中可能不被支持,需使用 polyfill 或其他替代方案。

总体来说,不同的导入和导出方式适用于不同的场景和需求。默认导出适用于只导出一个内容的情况,命名导出适用于导出多个内容的情况。导入全部可以集中管理导出的内容,而组合导出与导入则提供了更大的灵活性。动态导入适用于在运行时动态加载模块的特殊情况。根据实际需求选择合适的方式可以使代码更加清晰、可维护和可扩展。

 import导入模块和用require导入模块的区别

import是ES6引入的新特性,允许声明式的方式导入其他模块中的内容。require是Node.js中的特性,它允许你使用一个函数来加载和导入其他模块。

1.导入方式

import导入的方式是使用关键字import加上大括号(如果需要导入的内容是命名导出的话),再加上模块名的方式进行导入。

import { func1, func2 } from './myModule';

require导入的方式是使用require函数,将需要导入的模块路径作为参数传递给该函数。

const myModule = require('./myModule');

2.文件类型

import只能导入ES6模块或者使用Babel等工具转化为ES6模块的代码。而require则可以导入CommonJS模块、AMD模块、UMD模块以及Node.js内置模块等多种类型的模块。

3.变量提升

在ES6中,import语句是静态执行的,意味着它们在模块内部的顶层执行,并且在模块内部创建一个局部作用域。这意味着导入的变量只在模块内部可见,并且不会影响模块外部的变量。因此,使用import导入的变量是不会被提升的。

require函数是动态执行的,这意味着它在运行时执行,并且不会在模块内部创建一个局部作用域。因此,使用require导入的变量是可以被提升的。

如何理解import语句是静态执行的和require函数是动态执行的?

理解import语句是静态执行的和require函数是动态执行的,需要先了解这两个概念的含义。

静态执行是指在编译阶段就能够确定其执行结果的代码执行方式。在JavaScript中,import语句属于静态执行的代码,也就是说,当JavaScript引擎执行代码时,会在编译阶段对import语句进行静态分析,确定所导入的模块,并在运行时加载这些模块。

动态执行是指在运行时才能确定其执行结果的代码执行方式。在JavaScript中,require函数属于动态执行的代码,也就是说,当JavaScript引擎执行代码时,会在运行时动态地确定所需的模块,并加载这些模块。

因此,可以理解为:

  • import语句是静态执行的,因为在编译阶段就能够确定所导入的模块,从而在运行时快速加载这些模块。
  • require函数是动态执行的,因为在运行时才能够确定所需的模块,需要动态地加载这些模块。

值得注意的是,由于import语句是静态执行的,因此在代码中不能使用变量或表达式作为模块路径,而只能使用字符串字面量。而require函数则可以接受变量或表达式作为模块路径,从而动态地确定所需的模块。

4.导出方式 

importrequire在导出方式上也有一些区别。import使用ES6的导出方式,可以使用命名导出和默认导出两种方式进行导出。

// 命名导出
export function func1() {}

// 默认导出
export default {}

require使用CommonJS的导出方式,只能使用默认导出方式进行导出。

// 默认导出
module.exports = {};

require除了可以使用 module.exports 导出模块,还可以使用 exports 对象。实际上,exports 对象是 module.exports 的一个引用。当使用 exports 导出时,实际上是向 module.exports 对象添加属性和方法

5.模块作用域

在JavaScript中,每个模块都有自己的作用域,模块之间的变量是互相隔离的,不会相互干扰。这也是模块化编程的一个主要特点。

在使用import导入模块时,实际上是在模块内部创建了一个指向被导入模块的引用,而不是直接复制模块中的变量。因此,当不同的文件中使用import导入相同的模块时,它们实际上是共享了同一个模块实例,所以可以访问和修改同一个模块中的变量。

而在使用require导入模块时,实际上是将导入模块中的变量直接复制到(可以理解为浅拷贝)当前模块的作用域中。因此,当不同的文件中使用require导入相同的模块时,它们实际上是拥有各自独立的模块实例,彼此之间不会共享模块中的变量。

需要注意的是,如果使用require导入的模块中含有可变状态的对象,那么在不同文件中修改该对象的变量会相互影响。这也是require在某些情况下会产生一些难以预测的副作用的原因之一。而使用import导入的模块,由于是共享同一个模块实例,相对来说更容易管理和控制。

如果使用require导入的模块中含有可变状态的对象,比如一个对象的属性值可以被修改,那么当在不同的文件中修改这个对象中变量时,由于require会将导入的模块中的变量直接复制到当前模块的作用域中(类似于浅拷贝,模块中的普通变量(例如字符串、数字等)是非共享的,而对象的变量则是能被修改共用的。),因此会导致这个对象的变量在不同文件中的值相互影响。

举个例子,假设有一个config.js模块,其中定义了一个可变的对象config,并且在main.jsapp.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.jsapp.js时,它们都会通过require导入config.js模块,并且main.js中修改了config对象的port属性值。那么当app.js输出config.port时,它实际上输出的是被main.js修改后的port属性值,而不是config.js模块原本定义的值。这就是因为require导入的模块中含有可变状态的对象或变量,在不同文件中修改该对象或变量会相互影响的原因。

需要注意的是,这种影响是由于模块之间共享同一个对象的引用造成的,而不是模块本身的问题。因此,在编写模块时,需要谨慎地处理可变状态的对象,尽量避免在不同模块之间共享同一个对象的引用,以避免出现不可预测的副作用。

为了避免使用require导入的模块中含有可变状态的对象或变量,在不同文件中修改该对象或变量会相互影响的副作用,有以下方法:

  • 使用import代替require:使用import导入模块时,不同文件导入同一个模块实际上是共享了同一个模块实例,因此可以避免使用require时出现的副作用。

  • 使用纯函数:纯函数是指输入相同的参数,输出结果也相同,并且不会对外部环境产生任何副作用的函数。如果在模块中使用纯函数,那么即使该模块中的变量被修改了,但由于纯函数不会产生副作用,因此在不同文件中调用该函数时,输出结果也不会发生变化。

  • 使用常量或不可变对象:常量或不可变对象指的是一旦定义后就无法再被修改的值。如果在模块中使用常量或不可变对象,那么即使该模块中的变量被修改了,但由于常量或不可变对象无法被修改,因此在不同文件中调用该变量时,其值也不会发生变化。

  • 使用模块的副本:可以通过在模块导出时返回一个副本,而不是直接返回模块内部的对象或变量。这样在不同文件中使用require导入该模块时,它们会得到不同的副本,而不是共享同一个模块实例。

    // config.js
    let config = {
      env: 'dev',
      port: 3000
    }
    
    module.exports = Object.assign({}, config);

    在导出时使用Object.assign方法返回config对象的一个副本,从而避免了不同文件之间共享同一个模块实例的副作用。

 需要注意的是,这些方法并不是万无一失的,而是根据具体情况选用合适的方法来避免副作用。在编写模块时,需要考虑模块中的变量是否需要共享,是否可以被修改,以及模块在不同文件中被调用时可能产生的副作用等因素,从而选择合适的方法来避免副作用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值