JavaScript 中的模块化

最早的基于立即执行函数,闭包的模块化

const MountClickModule = function(){
  let num = 0;
  const handleClick = ()=>{
    console.log(++num);
  }   return {
    countClick:()=>{
      document.addEventListener('click',handleClick)
    }
  }
}(); MountClickModule.countClick(); (function(module) {
module.say = ()=>{
  console.log(num)  //undefined
  //do something
}
})(MountClickModule); MountClickModule.say();

这种闭包的坏处:

  • 扩展模块间无法访问私有变量。
  • 强依赖模块导入的顺序,项目变大后不好维护。

AMD 和 CommonJS 模块化JavaScript 应用

AMD 和 CommonJS 是两个互相竞争的标准,均可定义 JavaScript 模块。除了语法和原理的区别之外,主要区别是 AMD 的设计理念是明确基于浏览器,而 CommonJS 的设计是面向通用 JavaScript 环境

使用 AMD 定义模块依赖

AMD 异步模块定义规范制定了定义模块的规则,这样模块和模块的依赖可以被异步加载。这和浏览器的异步加载模块的环境刚好适应(浏览器同步加载模块会导致性能、可用性、调试和跨域访问等问题)。

目前,AMD 最流行的实现是 RequireJS

  define("alpha", ["require", "exports", "beta"], function (require, exports, beta) {
       exports.verb = function() {
           return beta.verb();
           //Or:
           return require("beta").verb();
       }
   });

AMD 提供名为 aplha 的函数,它接收一下参数:

  • 第一个参数,id,是个字符串。它指的是定义中模块的名字,这个参数是可选的。如果没有提供该参数,模块的名字应该默认为模块加载器请求的指定脚本的名字。
  • 第二个参数,dependencies,是个定义中模块所依赖模块的数组。
  • 第三个参数是初始化模块的工厂函数,该函数接收dependencies作为参数 AMD异步获取依赖,以避免阻塞,如果依赖来自服务器,那么这个过程将花费一些时间,知道依赖全部加载完成后,调用模块的工厂函数,并传入所有的依赖。

可以看出,AMD 有一下几项有点:

  • 异步加载模块,避免阻塞。
  • 自动处理依赖,我们无需考虑模块的引入顺序。
  • 在同一个文件中可以定义多个模块。

CMD

CMD 是 SeaJS 在推广过程中对模块定义的规范化产出,在 CMD 规范中,一个模块就是一个文件。代码的书写格式如下:

define(function (require, exports, module) {
  const foo = require('./foo')
  a.doSomething()
  // ...
  const bar = require('./bar') // 依赖可以就近书写
  b.doSomething()
  // do something else
})

CommonJS

AMD 的设计明确基于浏览器,而 CommonJS 的设计是面向通用 JavaScript 环境。CommonJS 目前在 Nodejs 社区中具有最多的用户。CommonJS 使用基于文件的模块,所以每个文件中都只能定义一个模块,CommonJs 提供变量 module,该变量具有属性 exports,通过 exports 很容易扩展属性。最后,module.exports 作为模块的公共接口。

const beta = require('beta');
function alpha(){
    return beta.verb();
    //Or:
    return require("beta").verb();
} module.exports = alpha;

CommonJS 要求一个文件就是一个模块,文件中的代码就是模块的一部分,所以不需要使用立即执行函数来包装变量,在模块中定义的变量都是安全的再模块中,不会泄露到全局作用域。只有通过 module.exports 对象暴露的对象或函数才可以在函数外部访问 CommonJS 具有以下特点:

  • 基于文件系统。
  • 引入模块时文件同步加载,可以访问模块的公共接口。
  • 模块加载相对更快 这是 CommonJS 在服务端更流行的原因。

ES6 模块的导入导出

ES6 模块结合了CommonJS 和 AMD 的有点,具体如下:

  • 与 CommonJS 类似,ES6模块语法相对简单,并且基于文件(每一个文件就是一个模块)
  • 与 AMD 类似,ES6 模块支持异步加载模块。

既 ES6 结合了两种模块化的有点,基于文件系统,既支持异步也支持同步,因为浏览器并没有实现 ES6 的模块化 API 所以具体是异步还是同步取决于loader api

ES6 模块的主要思想是必须显示的使用标志符导出模块,才能从外部访问模块。其他标志符,甚至在最顶级作用域中定义的标识符,只能在模块中使用。 ES6 引入两个关键字:

  • export ---- 从模块外部指定标识符。
  • import ---- 导入模块标识符。

从index.js模块中导出:

  const hello = 'hello';
  export const name = 'yunfly'   export function sayHi(){
    return `${hello} ${name}!`
  }

也可以在模块最后一起导出:

  // foo.js
  const hello = 'hello';
  export const name = 'yunfly'   export function sayHi(){
    return `${hello} ${name}!`
  }   export { name, sayHi }   // export { name as firstName, sayHi }
     // bar.js   // 使用 as 设置导如别名
  import { name as firstName, sayHi } from 'foo'   console.log(name)
  sayHi()   //bar2.js
  // 导出全部标识符:
  import * as sayModule from 'foo';   console.log(sayModule.name)
  sayModule.sayHi()

默认导出 export default 被认为是有害的

// foo.js
class Foo {} export default Foo // bar.js
import Foo from './foo';

这存在一些可维护性的问题:

  • 如果你在 foo.ts 里重构 Foo,在 bar.ts 文件中,它将不会被重新命名;
  • 如果你最终需要从 foo.ts 文件中导出更多有用的信息(在你的很多文件中都存在这种情景),那么你必须兼顾导入语法。
  • 在 ts 中默认导出的可发现性非常差,你不能智能的辨别一个模块它是否有默认导出。

文章参考:《JavaScript忍者秘籍》

JavaScript 中的模块化的更多相关文章

  1. 简单聊一聊Javascript中的模块化

    在面试中只要说到模块化的问题,多多少少总会问到这些,umd.amd.cjs.esm,可能听过其中一个两个,或者都听说过.接下来我们先简单了解一下他们到底是什么,又有什么样的区别呢. 最开始的时候,Ja ...

  2. JavaScript中的模块化之AMD和CMD

    前言: 为什么我们需要模块化开发,模块化开发的好处有哪些? 首先我们先说一下非模块化的开发方式带来的弊端. 非模块化开发中会导致一些问题的出现,变量和函数命名可能相同,会造成变量污染和冲突,并且出错时 ...

  3. JavaScript中为什么使用立即执行函数来封装模块?

    最近在学习JavaScript基础,在学习到面向对象编程时,学习到在JavaScript中实现模块化的方法,其中一个重要的点是如何封装私有变量. 实现封装私有变量的方法主要是: 使用构造函数 func ...

  4. javascript中模块化知识总结

    JavaScript 模块化开发 1. 模块化介绍 掌握模块化基本概念以及使用模块化带来的好处 当你的网站开发越来越复杂的时候,会经常遇到什么问题? 恼人的命名冲突 繁琐的文件依赖 历史上,JavaS ...

  5. JavaScript 中的数据类型

    Javascript中的数据类型有以下几种情况: 基本类型:string,number,boolean 特殊类型:undefined,null 引用类型:Object,Function,Date,Ar ...

  6. 【总结】浅谈JavaScript中的接口

    一.什么是接口 接口是面向对象JavaScript程序员的工具箱中最有用的工具之一.在设计模式中提出的可重用的面向对象设计的原则之一就是“针对接口编程而不是实现编程”,即我们所说的面向接口编程,这个概 ...

  7. JavaScript中的函数表达式

    在JavaScript中,函数是个非常重要的对象,函数通常有三种表现形式:函数声明,函数表达式和函数构造器创建的函数. 本文中主要看看函数表达式及其相关的知识点. 函数表达式 首先,看看函数表达式的表 ...

  8. 理解和使用 JavaScript 中的回调函数

    理解和使用 JavaScript 中的回调函数 标签: 回调函数指针js 2014-11-25 01:20 11506人阅读 评论(4) 收藏 举报  分类: JavaScript(4)    目录( ...

  9. [转]Javascript中的自执行函数表达式

    [转]Javascript中的自执行函数表达式 本文转载自:http://www.ghugo.com/javascript-auto-run-function/ 以下是正文: Posted on 20 ...

随机推荐

  1. Spring 容器的初始化

    读完这篇文章你将会收获到 了解到 Spring 容器初始化流程 ThreadLocal 在 Spring 中的最佳实践 面试中回答 Spring 容器初始化流程 引言 我们先从一个简单常见的代码入手分 ...

  2. tidyverse|数据分析常规操作-分组汇总(sumamrise+group_by)

    | 本文首发于 “生信补给站” https://mp.weixin.qq.com/s/tQt0ezYJj3H7x3aWZmKVEQ 使用tidyverse进行简单的数据处理: 盘一盘Tidyverse ...

  3. BERT模型图解

    转载于 腾讯Bugly 发表于 腾讯Bugly的专栏 原文链接:https://cloud.tencent.com/developer/article/1389555 本文首先介绍BERT模型要做什么 ...

  4. cas客户端流程详解(源码解析)--单点登录

    博主之前一直使用了cas客户端进行用户的单点登录操作,决定进行源码分析来看cas的整个流程,以便以后出现了问题还不知道是什么原因导致的 cas主要的形式就是通过过滤器的形式来实现的,来,贴上示例配置: ...

  5. Django2.0.6-Xadmin后台源码安装流程(python 3.8+django 2.0)

    1. 命令行执行 pip install git+git://github.com/sshwsfc/xadmin.git@django2 2.修改url.py 3.修改setting.py 4.卸载x ...

  6. 数据可视化之powerBI基础(二十)Power BI度量值和新建表,有什么异同?

    https://zhuanlan.zhihu.com/p/101812525 ​PowerBI中,有三个地方可以使用DAX,分别是度量值.新建列和新建表,这三个功能并成一排摆放在这里,如图所示, 之前 ...

  7. 数据可视化之powerBI技巧(二十二)利用这个方法,帮你搞定Power BI"增量刷新"

    Power BI的增量刷新功能现在已经对Pro用户开通,但由于种种限制,很多人依然无法使用无这个功能,所以,每一次刷新,都要彻底更新数据集.这对于量级比较大的数据集来说,着实是一件耗费时间的事情. 拿 ...

  8. Python之函数、递归、内置函数

    本节内容 1. 函数基本语法及特性 2. 参数与局部变量 3. 返回值 嵌套函数 4.递归 5.匿名函数 6.函数式编程介绍 7.高阶函数 8.内置函数 温故知新 1. 集合 主要作用: 去重 关系测 ...

  9. Python之协程、异步IO、redis缓存、rabbitMQ队列

    本节内容 Gevent协程 Select\Poll\Epoll异步IO与事件驱动 Python连接Mysql数据库操作 RabbitMQ队列 Redis\Memcached缓存 Paramiko SS ...

  10. linux专题(二):走近Linux系统 (2020-04-08 10:08)

    http://dwz.date/UDf 走近Linux系统 开机登录 开机会启动许多程序.它们在Windows叫做"服务"(service),在Linux就叫做"守护进程 ...