说下背景:

在ES6以前,JS语言没有模块化,如何让JS不止运行在浏览器,且能更有效的管理代码,

于是应运而生CommonJS这种规范,定义了三个全局变量:

require,exports,module

require 用于引入一个模块

exports 对外暴露模块的接口,可以是任何类型

module 是这个模块本身的对象

用require引入时获取的是这个模块对外暴露的接口(exports)

Node.js 使用了CommonJS规范:

在浏览器端,不像Node.js内部支持CommonJS,如何进行模块化,

于是出现了 CMD 与 AMD 两种方式,其主要代表是 seajs 和 requirejs,

他们都定义了一个全局函数 define 来创建一个模块:

可以看出CMD完好的保留了CommonJS的风格,

而AMD用了一种更简洁的依赖注入和函数返回的方式实现模块化。

两者除风格不同外最大区别在于加载依赖模块的方式,

CMD是懒加载,在require时才会加载依赖,

而AMD是预加载,在定义模块时就提前加载好所有依赖。

各有千秋,各有适合的场景,网上有两者详细评测和激烈的讨论。

背景介绍完,说正题。

我们要实现一个模块,让它既能在seajs(CMD)环境里引入,又能在requirejs(AMD)环境中引入,

当然也能在Node.js(CommonJS)中使用,另外还可以在没有模块化的环境中用script标签全局引入,

可谓是对write once,run anywhere的向往,实际上大部分npm的前端组件包也要考虑这个。

首先一个模块看起来应该是这样:

当然,模块输出的不止可以是对象,还是可以是任何值,包括一个类。

分析CMD和AMD,我们需要提供一个工厂函数传入define来定义模块,所以变成这样:

为适应Node.js,可以来判断全局变量,由于require在CMD和ADM中都有定义,所以只判断:

typeof module !== 'undefined' && typeof exports === 'object'

于是变成这样:

至此已经能够满足Node.js的需求。

当没有上述全局变量,且有define全局变量时,我们认为是AMD或CMD,可以直接将factory传入define:

注意:CMD其实也支持return返回模块接口,所以两者可以通用。

最后是script标签全局引入,我们可以将模块放在window上,

为了模块内部在浏览器和Node.js中都能使用全局对象,我们可以做此判断:

var global = typeof window !== 'undefined' ? window : global;

同时,我们用一个立刻执行的闭包函数将所有代码包含,来避免污染全局空间,

并将global对象传入闭包函数,最终变成这样:

注意:闭包前加上分号是为了给前一个模块填坑,分号多了没问题,少了则语句可能发生变化。

于是同一个js文件我们能愉快的在不同环境这样引入:

awesome!

写一个适应所有环境的js模块的更多相关文章

  1. 打算写一个《重学Node.js》系列,希望大家多多支持

    先放上链接吧,项目已经开始2周了:https://github.com/hellozhangran/happy-egg-server 想法 现在是2019年11月24日,还有人要开始学习Node.js ...

  2. 来,我们手写一个简易版的mock.js吧(模拟fetch && Ajax请求)

    预期的mock的使用方式 首先我们从使用的角度出发,思考编码过程 M1. 通过配置文件配置url和response M2. 自动检测环境为开发环境时启动Mock.js M3. mock代码能直接覆盖g ...

  3. 【良心保姆级教程】java手把手教你用swing写一个学生的增删改查模块

    很多刚入门的同学,不清楚如何用java.swing去开发出一个系统? 不清楚如何使用java代码去操作数据库进行增删改查一些列操作,不清楚java代码和数据库(mysql.sqlserver)之间怎么 ...

  4. 我发起了一个 用 javascript 写一个 Office 的 开源项目 JS Office

    用   js  写   Office,  说实在的,  把 现在已有的 各种 富文本编辑器 和 电子表格 js 库 收集起来 整合一下 就 差不多了 , 放到 前几天 那个 “  js 作为 一等公民 ...

  5. 前端与编译原理——用JS写一个JS解释器

    说起编译原理,印象往往只停留在本科时那些枯燥的课程和晦涩的概念.作为前端开发者,编译原理似乎离我们很远,对它的理解很可能仅仅局限于"抽象语法树(AST)".但这仅仅是个开头而已.编 ...

  6. 用weexplus从0到1写一个app

    说明 基于wexplus开发app是来新公司才接触的,之前只是用过weex体验过写demo,当时就被用vue技术栈来开发app的开发体验惊艳到了,这个开发体验比react native要好很多,对于我 ...

  7. 用weexplus从0到1写一个app(2)-页面跳转和文章列表及文章详情的编写

    说明 结束连续几天的加班,最近的项目终于告一段落,今天抽点时间开始继续写我这篇拖了很久的<用weexplus从0到1写一个app>系列文章.写这篇文章的时候,weexplus的作者已经把w ...

  8. 实现简单的 JS 模块加载器

    实现简单的 JS 模块加载器 1. 背景介绍 按需加载是前端性能优化的一个重要手段,按需加载的本质是从远程服务器加载一段JS代码(这里主要讨论JS,CSS或者其他资源大同小异),该JS代码就是一个模块 ...

  9. 如何发布一个自定义Node.js模块到NPM(详细步骤)

    咱们闲话不多说,直接开始! 由于我从没有使用过MAC,所以我不保证本文中介绍的操作与MAC一致. 文章开始我先假定各位已经在window全局安装了Node.js,下面开始进行详细步骤介绍: 本文本着, ...

随机推荐

  1. Windows Installer 服务启动错误 14007 的解决办法

    问题: 在 本地计算机 无法启动 Windows Installer 服务. 错误代码 14007: 在活动的激活上下文中没有找到任何查找密钥. 这个问题似乎涉及到 Windows Installer ...

  2. Linux LVM学习总结——扩展卷组VG

    Linux服务器由于应用变更或需求的缘故,有可能出现分区空间不足的情况,此时往往需要进行扩容(要增加分区的空间),而采用LVM的好处就是可以在不需停机的情况下可以方便地调整各个分区大小.如下所示,分区 ...

  3. git的学习

    学习怎么使用git这个分布式版本控制系统 学习资源 廖雪峰的git教程 Git的官方文档 Git命令列表 Git参考手册 中文 https://www.dropbox.com/s/sp2eupl8tp ...

  4. LCX端口转发实现内网突破

    工具:lcx.exe 原理:当目标主机仅开放了web服务,而该服务又仅能供内网用户使用,外网用户根本无法直接访问.因此想要让外网用户能能够访问局域网中的系统服务,必须进行端口映射等操作才行.其原理就是 ...

  5. Parseval's theorem 帕塞瓦尔定理

    Source: wiki: Parseval's theorem As for signal processing, the power within certain frequency band = ...

  6. [LeetCode] Dungeon Game 地牢游戏

    The demons had captured the princess (P) and imprisoned her in the bottom-right corner of a dungeon. ...

  7. Jquery揭秘系列:实现$.fn.extend 和$.extend函数

    前面我们扩展了bind方法和ready函数,这次我要讲一下$.fn.extend 和$.extend函数. 其他的不多说,直接切入主题吧! 先来看看这两个函数的区别: $.fn.extend是为查询的 ...

  8. HTML5射击类游戏----【地球保卫战】

    在线DEMO地址:打开: 游戏截图:      就不贴代码了, 因为代码太多了, 大概写一下这个游戏实现思路和一些实现: 游戏一共有三关, 每一关都有一个大Boss, Boss比较好杀,主要各种外星飞 ...

  9. PRINCE2特征(二)

    英国体系环境下项目有什么特征(二) 今天又要和大家分享了,这个时间也是自己很喜欢的时刻.上次给大家分享的是英国体系下项目的特征之一:临时性.不知道大家还有没有印象,英国体系下项目的特征有五个,今天来给 ...

  10. 【转】OpenGL超级宝典笔记——纹理映射Mipmap

    原文地址 http://my.oschina.net/sweetdark/blog/177812 , 感谢作者,若非法转载请联系本人. 目录[-] Mipmapping Mipmap过滤 构建Mip层 ...