https://blog.csdn.net/chehec2010/article/details/119804381

随着ES6的出现,js模块已经成为正式的标准了。曾经为了解决js模块问题而发展起来的民间秘籍,requireJs(AMD)、SeaJs(CMD)、Node(CommonJs),已经或者不久的将来会成为历史。了解历史也是很重要的,因为正式标准就是以民间秘籍为基础而发展起来的,有些规范仍然被广泛应用于开发中(CommonJS)。

ES6的class是面向对象的语法糖,升级了ES5的构造函数的原型链继承的写法,并没有解决模块化问题。Module功能则是为了解决这一问题而提出的。

在ES6之前,社区制定了一些模块加载方案,最主要的有CommonJS和AMD两种。CommonJS用于服务器,AMD用于浏览器。

ES6模块的设计思想是尽量静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。

ES6模块不是对象,而是通过export命令显式指定输出的代码,输入时也采用静态命令的形式。

什么是模块?

一个模块(module)就是一个文件。一个脚本就是一个模块。就这么简单。

模块可以相互加载,并可以使用特殊的指令 export 和 import 来交换功能,从另一个模块调用一个模块的函数:

  • export 关键字标记了可以从当前模块外部访问的变量和函数。
  • import 关键字允许从其他模块导入功能。
import { Fun1, Fun2, Fun3 } from 'file';

以上代码的实质是从file模块加载3个方法Fun1、Fun2和Fun3,其他方法不加载。这种加载方式称为编译时加载,即ES6能在编译时就完成模块编译。

一个模块就是一个独立的文件。该文件内部的所有变量,外部无法获取。如果想要外接获取模块内部的某个变量或方法,就必须使用export关键字输出该变量。

  1.  
    // profile.js
  2.  
    export var firstName = "John";
  3.  
    export var lastName = "Jackson";
  4.  
    export var year = 1999;

上述代码表示在profile.js文件(模块)中,输出了3个变量。

这种写法等价于:

  1.  
    var firstName = "John";
  2.  
    var lastName = "Jackson";
  3.  
    var year = 1999;
  4.  
    export { firstName, lastName, year };

在export命令后面使用大括号指定了所要输出的一组变量,等价于在每个变量前面加export。

export命令除了可以输出变量,也可以输出函数和类,写法相同。

export输出的变量就是本来的名字,但是可以使用as关键字重命名。

  1.  
    function f1() {}
  2.  
    function f2() {}
  3.  
    export { v1 as Fun1, v2 as Fun2, v2 as Foo };

可以使用as关键字对同一个变量或方法重命名两次,使其在引入模块中,可以使用不同的名字进行引用。

export命令可以出现在模块的任意顶层作用域的位置,不能出现在块级作用域内。

export语句输出的值是动态绑定的,绑定其所在的模块。

import命令

使用export命令定义了模块的对外接口后,其他JS文件就可以通过import命令加载这个模块的接口。

  1.  
    // main.js
  2.  
    import { firstName, lastName, year } from './profile';

import命令接受一个对象,里面指定了要从其他模块导入的变量名。对象中的变量名必须和要加载的模块导出的接口变量名一致(如果接口没有使用as关键字,就使用原始变量名,如果使用as关键字,则使用重命名后的接口名)。

同理,如果要对引入的变量名进行重命名,可以在import命令中使用as关键字,将输入的变量重命名。

  1.  
    // main.js
  2.  
    import { firstName as surname } from './profile';

上述写法中,需要书写每个接口的变量名,如果是要对整个模块进行整体加载,可以使用星号(*)指定一个对象,将输出模块的所有接口都加载到这个对象上。

  1.  
    // main.js
  2.  
    import * as person from './profile';

import命令具有提升效果,会提升到整个模块的顶部首先执行。

export default命令

import命令在加载变量名或函数名时,需要事先知道导出的模块的接口名,否则无法加载。可以使用export default命令指定模块的默认输出接口。

  1.  
    // profile.js
  2.  
    export default function () {
  3.  
    console.log("my name is John Jackson, I was born in 1999");
  4.  
    }

上述代码中,profile模块默认输出的是一个函数。这样,导入模块就可以不用指定要加载的接口名了。

  1.  
    // main.js
  2.  
    import myName from './profile';
  3.  
    myName(); // "my name is John Jackson, I was born in 1999"

在main.js文件中,myName指代的就是profile文件输出的默认接口。这意味着import命令可以用任意名称指向profile文件输出的默认接口,而不需要知道接口名。

export default命令用在非匿名函数前也是可以的,此时函数名在模块外部是无效的,加载时视同匿名函数。

  1.  
    // profile.js
  2.  
    export default function sayName () {
  3.  
    console.log("my name is John Jackson, I was born in 1999");
  4.  
    }
  5.  
     
  6.  
    // main.js
  7.  
    import myName from './profile';
  8.  
    myName(); // "my name is John Jackson, I was born in 1999"

一个模块只能有一个默认输出,因此export default在一个模块中只能使用一次。所以,对应的import命令可以不加大括号。

如果要在一条import命令中同时引入默认方法和其他变量,可以写成以下这样:

import customName, { otherMethod } from './module-name';

customName指代默认接口的命名,otherMethod指代其他接口。

模块的继承

模块之间可以继承。假设有个Circle模块继承了Shape模块。

  1.  
    // cicle.js
  2.  
    export * from 'Shape';
  3.  
    export var pi = 3.14159265359;
  4.  
    export default function area(r) {
  5.  
    return pi * r * r;
  6.  
    }

export * 表示输出模块Shape的所有属性和方法,但不会输出Shape的默认方法。

module命令

如果要整体加载模块,可以使用module命令代替上述的import * as命令。module命令不会加载模块的默认方法。需要额外使用import命令加载模块的默认方法。

  1.  
    // main.js
  2.  
    module person from './profile';

ES6模块加载的实质

ES6模块输出的是值的引用。

ES6模块遇到模块加载命令import时不会去执行模块,只会生成一个动态的只读引用。等到真的需要用到时,再到模块中取值。

ES6的输入有点像UNIX系统的“符号链接”,原始值变了,输入值也会跟着变。因此,ES6模块是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块。

  1.  
    // lib.js
  2.  
    export let count = 3;
  3.  
    export function add() { count++; }
  4.  
     
  5.  
    // main.js
  6.  
    import { count, add } from './lib';
  7.  
    console.log(count); // 3
  8.  
    add();
  9.  
    console.log(count); // 4

由于ES6输入的模块变量只是一个“符号链接”,所以这个变量是只读的,对它进行重新赋值会报TypeError异常。

  1.  
    // lib.js
  2.  
    export let obj = {};
  3.  
     
  4.  
    // main.js
  5.  
    import { obj } from './lib';
  6.  
    obj.prop = 123; // OK
  7.  
    obj = {}; // TypeError

obj指向的地址是只读的,无法为其重新赋值。

模块接口的导出

模块接口的导入

javascript模块 (Module) 简介的更多相关文章

  1. ES6新特性6:模块Module

    本文摘自ECMAScript6入门,转载请注明出处. 一.Module简介 ES6的Class只是面向对象编程的语法糖,升级了ES5的构造函数的原型链继承的写法,并没有解决模块化问题.Module功能 ...

  2. (转)深入理解JavaScript 模块模式

    深入理解JavaScript 模块模式 (原文)http://www.cnblogs.com/starweb/archive/2013/02/17/2914023.html 英文:http://www ...

  3. Javascript模块规范(CommonJS规范&&AMD规范)

    Javascript模块化编程(AMD&CommonJS) 前端模块化开发的价值:https://github.com/seajs/seajs/issues/547 模块的写法 查看 AMD规 ...

  4. 转: javascript模块加载框架seajs详解

    javascript模块加载框架seajs详解 SeaJS是一个遵循commonJS规范的javascript模块加载框架,可以实现javascript的模块化开发和模块化加载(模块可按需加载或全部加 ...

  5. javascript模块加载框架seajs详解

    SeaJS是一个遵循commonJS规范的javascript模块加载框架,可以实现javascript的模块化开发和模块化加载(模块可按需加载或全部加载).SeaJS可以和jQuery完美集成,使用 ...

  6. javascript 模块

    一.模块 function foo() { var something = "cool"; var another = [1, 2, 3]; function doSomethin ...

  7. 小矮人Javascript模块加载器

    https://github.com/miniflycn/webkit-dwarf 短小精悍的webkit浏览器Javascript模块加载器 Why 我们有许多仅基于webkit浏览器开发的应用 无 ...

  8. javascript闭包(Module模式)的用途和高级使用方式

    javascript闭包(Module模式)的用途和高级使用方式 javascript闭包的用途:1. 匿名自执行函数:或者可以理解为,避免污染全局变量2. 缓存:源于闭包的核心特性便是保存状态,应用 ...

  9. RequireJS 是一个JavaScript模块加载器

    RequireJS 是一个JavaScript模块加载器.它非常适合在浏览器中使用, 它非常适合在浏览器中使用,但它也可以用在其他脚本环境, 就像 Rhino and Node. 使用RequireJ ...

  10. 编写浏览器和Node.js通用的JavaScript模块

    长期以来JavaScript语言本身不提供模块化的支持, ES6中终于给出了 from, import等关键字来进行模块化的代码组织. 但CommonJS.AMD等规范已经被广为使用,如果希望你的Ja ...

随机推荐

  1. 从Clipto.AI看AI SaaS创业的隐形机会:一个月2500万访问量背后的商业逻辑

    最近深度研究了一个让我眼前一亮的产品--Clipto.AI. 这款看似简单的音视频转录工具,月访问量竟然达到了2540万,这个数字让我震惊,也让我重新思考了AI工具的商业化路径. 今天想和大家分享一下 ...

  2. 人形机器人全能赛openmv巡线代码

    人形机器人全能赛openmv巡线代码 import sensor, image, time from pyb import LED, millis, UART from math import pi, ...

  3. 服务熔断&服务降级

    服务熔断 在服务消费方做出的处理,主要针对服务提供方发生异常(500)或者服务提供方故障(宕机)或者连接服务提供方超时等情况,做出的预备方案(在服务消费方调用服务提供方异常的情况下,执行准备好的Fal ...

  4. VS2022 下载超详细安装教程(附安装包及秘钥):全能开发工具部署指南

    目录 一.VS2022软件核心功能与优势 二.VS2022下载及安装准备 1. 系统要求: 2. VS2022下载: 三.VS2022详细安装步骤 1. 解压VS2022安装包 2. 运行VS2022 ...

  5. 二、Linux基本应用工具

    1.系统文件共享(网络) 通过网络文件共享协议(例如 SMB 或 NFS)来完成Ubuntu下的文件夹共享给 Windows 1.Samba 实现共享 安装samaba sudo apt update ...

  6. 速看!新版SpringAI的2个致命问题

    无论是使用最新正式版的 Spring AI,还是最新正式版 Spring AI Alibaba,在实现自定义 MCP 服务器端和客户端的时候,一定要注意这两个问题,不然你会发现你的 MCP 服务器端能 ...

  7. donNet 文件上传下载进度计算(一段代码体现数学在编码中的重要位置)

    上传进度: var 每次成功增加的进度 = Convert.ToDouble(文件已上传大小) / Convert.ToDouble(文件总大小); var 当前进度 = (每次成功增加的进度 *10 ...

  8. C# DateTime时间戳帮助类型

    https://www.cnblogs.com/minotauros/p/10773258.html /// <summary> /// 时间工具类 /// </summary> ...

  9. CF1989C Two Movies 题解

    CF1989C Two Movies 贪心.如果某人对两部电影评分不一样,显然取评分高的那一个.如果是 \(1\) 和 \(0\) 或 \(1\) 和 \(-1\),那么显然取 \(1\).如果是 \ ...

  10. eslint vscode 配置

    简介 以作备份 step1 vscode 安装插件 eslint step2 file->preferences->setting code Actions On Save "e ...