第11章 代码模块化

1. 在ES6之前的版本中模块化代码

  • 每个模块系统至少应该能够执行一下操作:
    • 定义模块接口,通过接口可以调用模块的功能
    • 隐藏模块的内部实现,使模块的使用者不需要关注模块内部的实现细节。同时,吟唱模块的内部实现,避免有可能产生的副作用和对bug的不必要修改

1.1 使用对象、闭包和立即执行函数实现模块

使用函数作为模块

(function countClicks() {
    let numClicks = 0;  // 这个变量只有通过事件处理器调用,屏蔽了外部访问
    // 事件处理器创建的闭包保持局部变量numClicks活跃
    document.addEventListener("click", () => {
        numClicks++;
        console.log(numClicks);
    });
})();
// 暂时无法满足模块的第一个要求:接口

模块模式:使用函数扩展模块,使用对象实现接口

// 本例同样使用立即执行函数,
// 但通过返回对象的方式将可以暴露的内容返给了MouseCounterModule变量
// 使其成为一个接口,可以访问内部功能
// 模块内部细节可以通过接口创建的闭包保持活跃
const MouseCounterModule = function() {
    let numClick = 0;
    const handleClick = () => {
        console.log(++numClick);
    }

    const print = message => console.log(message);

    return {
        countClicks: () => {
            document.addEventListener("click", handleClick);
        },
        print: print
    };
}();    // 立即执行函数

MouseCounterModule.countClicks();
MouseCounterModule.print("Hello!");
// Hello!

模块扩展

// 使用立即执行函数
// 将要扩展的模块作为参数传入函数
(function(module) {
    let numScroll = 0;
    const handleScroll = () => {
        console.log(++numScroll);
    }

    // 将要添加的功能添加到传入模块当中
    module.countScroll = () => {
        document.addEventListener("wheel", handleScroll);
    }
})(MouseCounterModule);

MouseCounterModule.countScroll();

通过模块扩展无法共享模块的私有变量
当开始创建模块化应用时,模块本身常常依赖其他模块的功能,而模块模式无法实现这些依赖关系。

1.2 使用AMD和CommonJS模块化JS代码

AMD

  • 设计明确基于浏览器
  • 自动处理依赖,无需考虑模块引入的顺序
  • 异步加载模块,避免阻塞
  • 在同一文件夹中可以定义多个模块

CommonJS

  • 基于文件,面向通用JS环境,不显式支持浏览器环境
  • 一个文件就是一个模块,文件中的代码就是模块的一部分,不需要使用立即执行函数来包装变量
  • 语法简单,只需定义module.exports属性,其余与标准代码无差异,引用模块只需使用require函数
  • 是Node.js默认的模块格式,所以可以使用npm无数的包

2. ES6模块

导出和导入功能

在浏览器环境中使用时,文件类型需要设置为module

<script src="./js/index.js" type="module"></script>
// info.js
const name = "Wango";

// 单独导出内容
export const age = 24;

export function say(message){
    console.log(message);
}
// 或者
const name = "Wango";

const age = 24;

function say(message){
    console.log(message);
}

export {age, say}; // 推荐


// index.js

// 导入的内容必须用大括号包起来
// 路径是字符串且必须以.或./或../开头
import { say } from "./info.js";

say("Hello!");
// Hello!

// 导入这个文件全部标识符,且取个别名(不用大括号)
import * as info from "./info.js";

info.say("Hello!!!");
// Hello!!!

默认导出

// Student.js
// 定义默认导出
export default class Student {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
    info() {
        return `Name: ${this.name} | Age: ${this.age}`;
    }
}
// 默认导出的同时也可以导出指定内容
export function say(msg) {
    console.log(msg);
}

// index.js
// 导入默认导出的内容不需要大括号,也可以起别名
import Stu from "./Student.js";

const s1 = new Stu("Wango", 24);
console.log(s1.info());
// Name: Wango | Age: 24

// 同时导入默认导出和普通导出
import Stu, { say } from "./Student.js";

const s1 = new Stu("Wango", 24);
console.log(s1.info());
// Name: Wango | Age: 24

say("Hello!");
// hello

导入或导出时使用重命名

// Students.js
export default class Student {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
    info() {
        return `Name: ${this.name} | Age: ${this.age}`;
    }
}

function say(msg) {
    console.log(msg);
}

// 用as设置别名
export {say as sayHello};

// index.js
import Stu, { say as sayHi } from "./Student.js";

const s1 = new Stu("Wango", 24);
console.log(s1.info());
// Name: Wango | Age: 24

sayHi("Hello!");
// hello

使用as设置了别名之后就只能访问别名,不能访问原始的标识符

posted @ 2020-11-28 00:05  LiuWango  阅读(343)  评论(0编辑  收藏  举报