Module

Module

在es6之前,JavaScript没有模块系统。
社区自己定制了一些模块加载方案,如CommonJS(服务器)、AMD(浏览器)等。

模块化

1.原始写法

函数分文件存放

function A(){}

function B(){}

缺点: 多 人 开 发 容 易 造 成 全 局 变 量 污 染 \color{#fa4}多人开发容易造成全局变量污染

2.对象写法

  var moduleA = {
        count:10,
        A:function () {
            this.count +=10;
            console.log(this.count)
        },
        B:function () {
            this.count *=10;
            console.log(this.count)
        }
    };
    var moduleB = {
        count:20,
        A:function () {
            this.count -=10;
            console.log(this.count)
        },
        B:function () {
            this.count /=10;
            console.log(this.count)
        }
    };
    moduleA.A()
    moduleA.B()
    moduleB.A()
    moduleB.B()

Module_第1张图片
在函数调用之前改变moduleA的count值,关于moduleA的所有运算都会改变

...
	moduleA.count = "haha"
    moduleA.A()
    moduleA.B()
    moduleB.A()
    moduleB.B()

Module_第2张图片

缺点: 函 数 和 变 量 都 是 对 外 暴 露 的 \color{#fa4}函数和变量都是对外暴露的

3.闭包(立即执行函数)

    var moduleA = (function () {
        var count = 10;
        function A() {
           count +=10;
           console.log(count)
        }
        function B() {
            count *=10;
            console.log(count)
        }
        return {
            showA:A,
            showB:B
        }
    })()
    moduleA.showA();
    moduleA.showB();

外部访问不到count变量和A访问,只能通过return返回的对象访问moudleA.showA

...
moduleA.showA();
moduleA.showB();
console.log(moduleA.count)
moduleA.A()

Module_第3张图片
闭包中变量为私有变量,函数为私有函数,避免了全局污染
缺点: 无 法 获 取 内 部 变 量 和 函 数 , 故 无 法 拓 展 , 只 能 改 源 码 \color{#fa4}无法获取内部变量和函数,故无法拓展,只能改源码

模块化的优点:

  • 降低程序之间的耦合性
  • 方便代码复用
  • 维护方便

CommonJS

一个js文件就是一个模块,模块中的代码是全部包装在函数中的

可以通过类数组arguments对象验证,只有在函数中才能使用arguments对象

  • arguments对象和Function是分不开的
  • 因为arguments这个对象不能显式创建
  • arguments对象只有函数开始时才可用
    Module_第4张图片
  • arguments.length为函数实参个数,arguments.callee引用函数自身
    Module_第5张图片
    一个模块是一个同时传递5个(exports,require,module,__filename,__dirname)实参的函数

exports 该对象用来将变量或函数暴露到外部

require 函数,用来引入外部函数

module 代表当前模块本身,exports就是module的属性,使用module.exports导出与exports导出 实质上是一样的
Module_第6张图片

__filename 当前文件的完整路径

__dirname 当前模块所在文件夹的路径

e x p o r t s 和 m o u d l e . e x p o r t s 区 别 \color{#f4a}exports 和moudle.exports区别 exportsmoudle.exports

  • 通过exports只能使用"."的的方式来向外暴露内部变量

    exports===module.exports 实质上是 exports中存放的是module.exports的内存地址。exprots.的方式是改变内存地址中的变量即module.exports对象中的变量,而exports直接赋值,改变的是exports中存放的module.exports的内存地址,即exports不再指向module.exports。exports从引用数据类型变为基本数据类型了。所以会找不到预期暴露的对象或函数

  • module.exports既可以通过.的形式,也可以直接赋值

通过require()方法,用于加载模块。require()可以传递一个文件的路径作为参数。require()引入模块后返回一个(代表引入的模块)对象引入的模块必须通过exports属性将其暴露出去。Module_第7张图片Module_第8张图片

AMD

Asynchronous Module Definition(异步模块加载机制)。描述了模块的定义,依赖关系,引用关系以及加载机制。requireJS

define(id?,dependencies,factory)
  • define函数是全局变量

  • id 指定了被定义模块的id。(可选)

    可选,字符串类型,定义模块标识,如果没有提供参数,默认为文件名

  • 依赖(可选)

    字符串数组,AMD 推崇依赖前置,即当前模块依赖的其他模块,模块依赖必须在真正执 行具体的factory方法前解决

  • factory(必需)

    工厂方法,初始化模块需要执行的函数或对象。如果为函数,它只被执行一次。如果是对象,此对象会作为模块的输出值。

requireJs

script标签中的属性

defer【ie】 async = 'true'引入的所有.js文件都是异步执行的

异步加载,渲染引擎遇到异步加载的文件,就会开始下载外部脚本,但不会等他下载和执行,而是直接执行后面的命令
defer要等到整个页面在内存中正常渲染结束,才会执行
async 文件一旦下载完,渲染引擎就会中断渲染,执行这个脚本以后,再继续渲染。
多个defer脚本,会按照它们在页面出现的顺序加载,而多个async脚本是不能保证加载顺序的

data-main=' '设置入口文件
每一个.html文件都有有一个入口文件,管理当前.html页面使用的所有的.js代码
后续引入的所有.js,后缀都可以省略
创建模块

define(function () {
    function add(x,y) {
        return x + y
    }
    function show() {
        console.log("hello world")
    }
    return {
        add:add,
        show:show
    }
})

引入模块

require.config({
    paths:{
        add:'demo/add',
    }
})
require(['add'],function (add) {
    var res = add.add(1,2)
    console.log(res)
    add.show()
})

如果引入的模块由依赖关系比如jquery-cookie依赖与jquery,在require.config 中添加如下配置

shim:{
    //设置依赖关系 先引入jquery.js 然后在引入jquery-cookie
	"jquery-cookie":["jquery"],
	//声明当前模块不遵从AMD
	"XXX":{
		exports:"_"
	}
}

module

es6的模块都默认是严格模式

  • export命令

    对外暴露模块接口

    export var firstName = "xxx"
    export var lastName = 'Jackson';
    export var year = 1958;
    
    var firstName = "xxx"
    var lastName = 'Jackson';
    var year = 1958;
    export { firstName, lastName, year  }
    
    export function add(x,y){ return x + y }
    

    重命名

    function v1(){}
    function v2(){}
    export {
    	v1 as streamV1
    	v2 as streamV2
    };
    
  • import命令

    引入模块功能
    使用export定义的模块对外接口后,可以使用import加载这个模

    import { firstName, lastName, year} from './xxx'
    

    重命名

    import { firstName as surname } from './xxx'
    

    i m p o r t 命 令 输 入 的 变 量 都 是 只 读 的 \color{#fa4}import命令输入的变量都是只读的 import
    i m p o r t 命 令 具 有 提 升 效 果 , 会 提 升 到 整 个 模 块 的 头 部 , 首 先 执 行 \color{#fa4}import命令具有提升效果,会提升到整个模块的头部,首先执行 import
    i m p o r t 是 静 态 执 行 , 所 以 不 能 使 用 表 达 式 和 变 量 \color{#fa4}import是静态执行,所以不能使用表达式和变量 import使
    i m p o r t 语 句 会 执 行 所 加 载 的 模 块 \color{#fa4}import语句会执行所加载的模块 import

模块整体加载

即用星号(*)指定一个对象,所有输出值都加载在这个对象上面。

import * as circle from './circle';

export default 命令

// export-default.js
export default function () {
  console.log('foo');
}

其他模块加载该模块时,import命令可以为该匿名函数指定任意名字

// import-default.js
import customName from './export-default';
customName(); // 'foo'
// 第一组
export default function crc32() { // 输出
  // ...
}

import crc32 from 'crc32'; // 输入

// 第二组
export function crc32() { // 输出
  // ...
};

import {crc32} from 'crc32'; // 输入

使 用 e x p o r t d e f a u l ‘ 时 , 对 应 的 i m p o r t 语 句 不 需 要 使 用 大 括 号 \color{#fa4}使用export defaul`时,对应的import语句不需要使用大括号 使exportdefaulimport使
e x p o r t d e f a u l t 命 令 其 实 只 是 输 出 一 个 叫 做 d e f a u l t 的 变 量 , 所 以 它 后 面 不 能 跟 变 量 声 明 语 句 \color{#fa4}export default命令其实只是输出一个叫做default的变量,所以它后面不能跟变量声明语句 exportdefaultdefault


var a = 1;
export default a;

e x p o r t d e f a u l t a 的 含 义 是 将 变 量 a 的 值 赋 给 变 量 d e f a u l t \color{#fa4}export default a的含义是将变量a的值赋给变量default exportdefaultaadefault
export default命令的本质是将后面的值,赋给default变量

export default function (obj) {
  // ···
}

export function each(obj, iterator, context) {
  // ···
}

export { each as forEach };

同时输入默认方法和其他接口

import _, { each, forEach } from 'lodash';

export 与 import的复合写法

export { foo, bar } from 'my_module';

// 可以简单理解为
import { foo, bar } from 'my_module';
export { foo, bar };

foo和bar实际上并没有被导入当前模块,只是相当于对外转发了这两个接口,导致当前模块不能直接使用foo和bar。

具名接口改为默认接口

export { es6 as default } from './someModule';

// 等同于
import { es6 } from './someModule';
export default es6;

默认接口也可以改名为具名接口

export { default as es6 } from './someModule';

ES6与CommonJS模块的差异

  • CommonJS模块输出的是一个 值 的 拷 贝 \color{#42c60b}值的拷贝 ,es6模块输出的是 值 的 引 用 \color{#42c60b}值的引用
  • CommonJS模块是 运 行 时 加 载 \color{#42c60b}运行时加载 ,es6模块时 编 译 时 输 出 接 口 \color{#42c60b}编译时输出接口
  • CommonJS模块的 r e q u i r e ( ) 是 同 步 加 载 模 块 \color{#42c60b}require()是同步加载模块 require(),es6模块的 i m p o r t 命 令 是 异 步 加 载 \color{#42c60b}import命令是异步加载 import,有一个独立的模块依赖解析阶段
  • CommonJS 加载的是一个对象(即module.exports属性),该对象只有在脚本运行完才会生成。ES6 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。

ES6 模块的运行机制与 CommonJS 不一样。JS 引擎对脚本静态分析的时候,遇到模块加载命令import,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。
ES6 模块不会缓存运行结果,而是 动 态 地 去 被 加 载 的 模 块 取 值 \color{#42c60b}动态地去被加载的模块取值 ,并且变量总是绑定其所在的模块。

package.json的main字段

package.json文件有两个字段可以指定模块的入口文件

  • main
{
  "type": "module",
  "main": "./src/index.js"
}

项目的入口脚本为./src/index.js,它的格式为 ES6 模块。如果没有type字段,index.js就会被解释为 CommonJS 模块。然后就可以使用import命令就可以加载这个模块

  • exports

    优先级高于main

    • 字目录别名
      exports字段可以指定脚本或字目录的别名
      //./node_modules/es-module-package/package.json
      {
        "exports": {
          "./submodule": "./src/submodule.js"
        }
      }
      
      使用别名加载这个模块
      import submodule from 'es-module-package/submodule';
      //./node_modules/es-module-package/src/submodule.js
      
      如果没有指定别名,就不能用“模块+脚本名”这种形式加载脚本。
    • main 的别名
      exports字段的别名如果是.,就代表模块的主入口,优先级高于main字段,并且可以直接简写成exports字段的值。
      {
        "exports": {
          ".": "./main.js"
        }
      }
      
      // 等同于
      {
        "exports": "./main.js"
      }
      
      exports字段只有支持ES6的node.js才认识
      {
        "main": "./main-legacy.cjs",
        "exports": {
          ".": "./main-modern.cjs"
        }
      }
      
    • 条件加载
      利用.这个别名,可以为 ES6 模块和 CommonJS 指定不同的入口。目前,这个功能需要在 Node.js 运行的时候,打开–experimental-conditional-exports标志。
      {
        "type": "module",
        "exports": {
          ".": {
            "require": "./main.cjs",
            "default": "./main.js"
          }
        }
      }
      

CommonJS模块加载ES6模块

CommonJS 的require()命令不能加载 ES6 模块,会报错,只能使用import()这个方法加载。

(async ()=>{
	await import('./xxx)
})()

ES6模块加载Commonjs模块

ES6 模块的import命令可以加载 CommonJS 模块,但是 只 能 整 体 加 载 \color{#42c60b}只能整体加载 ,不能只加载单一的输出项。

import packageMain from 'commonjs-package';

你可能感兴趣的:(es6,js,笔记,javascript,前端,开发语言)