javascript基础修炼(4)——UMD规范的代码推演
1. UMD规范
地址:https://github.com/umdjs/umdjavascript
UMD
规范,就是全部规范里长得最丑的那个,没有之一!!!它是为了让模块同时兼容AMD
和CommonJs
规范而出现的,多被一些须要同时支持浏览器端和服务端引用的第三方库所使用。UMD
是一个时代的产物,当各类环境最终实现ES harmony
的统一的规范后,它也将退出历史舞台。前端
UMD
规范的结构乍一看很是复杂,主要是由于想要看懂这段范式须要一些javascript
基础知识,它的基本结构是这样的:java
(function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD define(['jquery', 'underscore'], factory); } else if (typeof exports === 'object') { // Node, CommonJS之类的 module.exports = factory(require('jquery'), require('underscore')); } else { // 浏览器全局变量(root 即 window) root.returnExports = factory(root.jQuery, root._); } }(this, function ($, _) { // 方法 function a(){}; // 私有方法,由于它没被返回 (见下面) function b(){}; // 公共方法,由于被返回了 function c(){}; // 公共方法,由于被返回了 // 暴露公共方法 return { b: b, c: c } }));
2. 源码范式推演
2.1 基本结构
先来看最外层的结构:node
(function (){}());
很是简单,就是一个自执行函数。既然它是一个模块化的标准,也就意味着这个自执行函数最终能够导出一个模块,那么从代码的角度来说实际上有两种常见的实现方式:jquery
- return返回一个模块;
- 实参传入一个对象,把函数内部生成好的须要导出的东西挂在这个对象的属性上;
能够看到上面的函数体内部是没有return语句的,那么能够猜想UMD
在实现时是采用了第二种方式。既然UMD
是一种模块化的规范,那么它的功能就是根据使用要求生产模块,也就是说它的职责定位叫作模块工厂,咱们能够定义一个factory
方法,每当执行该方法时,就回返回一个模块,因此它的基本结构就变成了以下的样子:git
(function (factory){ //假设没有使用任何模块化方案,那么将工厂函数执行后返回的内容直接挂载到全局 window.Some_Attr = factory(); }(function(){ //自定义模块主体的内容 /* var a,b,c function a1(){} function b1(){} function c1(){} return { a:a1, b:b1 } */ }))
也就是说咱们自定义一个匿名函数,而后把它当作实参传给了自执行函数,而后在自执行函数内部经过形参来访问这个工厂方法
(或者你会更熟悉回调函数
或callback
这样的叫法),把它简单地挂在到全局对象上,这样就完成了基本的模块导出。github
有的时候咱们也但愿能够将模块挂载到非全局的环境,将挂载对象动态传入可让代码变得更灵活,此处涉及到一个基础知识,就是浏览器环境中的全局对象拥有parent
,top
,self
三个属性来追踪页面中嵌入<iframe>
后引入的新的Window对象的,单页面Window.self是指向本身的,代码中常经过是否包含self
属性来鉴别全局对象,因此此处的写法能够改进为兼容:api
(function(root,factory){ root.Some_Attr = factory(); }(self !== undefined ? self : this, function(){ }));
2.2 适配AMD
接着咱们先来加入AMD
的规范的适配,规范地址:AMD规范github地址:浏览器
/* * AMD规范的模块定义格式是define(id?, dependencies?, factory),factory就是实际的模块内容 */ (function (factory){ //判断全局环境是否支持AMD标准 if(typeof define === 'function' && define.amd){ //定义一个AMD模块 define([/*denpendencies*/],factory); } }(function(/*formal parameters*/){ //自定义模块主体的内容 /* var a,b,c function a1(){} function b1(){} function c1(){} return { a:a1, b:b1 } */ }))
2.3 适配CommonJs
接着咱们先来加入CommonJs
的规范的适配:ide
/* * CommonJs规范使用require('moduleName')的格式来引用模块,使用module.exports对象输出模块,因此只要把模块的输出内容挂载到module.exports上就完成了模块定义。 */ (function (factory){ //判断全局环境是否支持CommonJs标准 if(typeof exports === 'object' && typeof define !== 'function'){ module.exports = factory(/*require(moduleA), require(moduleB)*/); } }(function(/*formal parameters*/){ //自定义模块主体的内容 /* var a,b,c function a1(){} function b1(){} function c1(){} return { a:a1, b:b1 } */ }))
加入对CommonJs
的适配后,函数主体中return的内容(通常是一个对象)就被挂载到了module.exports
上,若是你编写过node.js
代码,对此必定不会陌生。
把上面的片断揉到一块,你也就看懂
UMD
的样子了。
3. 更具针对性的UMD范式
UMD
在其github主页上提供了更具针对性的范式,适用于不一样的场景,感兴趣的读者能够自行查看(地址在第一节已经给出)。
在此贴一个可能对大多数开发者比较有用的jqueryPlugin
的开发范式,若是看懂了上面的分析,那么下面的代码应该不难看懂:
// Uses CommonJS, AMD or browser globals to create a jQuery plugin. (function (factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module. define(['jquery'], factory); } else if (typeof module === 'object' && module.exports) { // Node/CommonJS module.exports = function( root, jQuery ) { if ( jQuery === undefined ) { // require('jQuery') returns a factory that requires window to // build a jQuery instance, we normalize how we use modules // that require this pattern but the window provided is a noop // if it's defined (how jquery works) if ( typeof window !== 'undefined' ) { jQuery = require('jquery'); } else { jQuery = require('jquery')(root); } } factory(jQuery); return jQuery; }; } else { // Browser globals factory(jQuery); } }(function ($) { $.fn.jqueryPlugin = function () { return true; }; }));
4. 模块化开发
前端模块化自己是一个稍显混乱的话题,笔者本身最初也是require( )
和require.js
傻傻分不清楚,但模块化是前端开发中很是重要的课题,若是你不想一生只是在一个页面内写代码,这一关是必定要过的,感兴趣的读者能够按照下面的基本类别划分分块进行学习。