前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >jQuery源码研究:模块规范兼容

jQuery源码研究:模块规范兼容

作者头像
前端_AWhile
发布2019-08-29 13:14:01
1.1K0
发布2019-08-29 13:14:01
举报
文章被收录于专栏:前端一会

今天开始研究jquery源码。

从jq官网down下最新的未压缩版代码并打开后,首先看下整体,这就是一个大型的自执行的匿名函数:

代码语言:javascript
复制
 1( function( global, factory ) {
 2
 3    "use strict";
 4
 5    if ( typeof module === "object" && typeof module.exports === "object" ) {
 6
 7        // For CommonJS and CommonJS-like environments where a proper `window`
 8        // is present, execute the factory and get jQuery.
 9        // For environments that do not have a `window` with a `document`
10        // (such as Node.js), expose a factory as module.exports.
11        // This accentuates the need for the creation of a real `window`.
12        // e.g. var jQuery = require("jquery")(window);
13        // See ticket #14549 for more info.
14        module.exports = global.document ?
15            factory( global, true ) :
16            function( w ) {
17                if ( !w.document ) {
18                    throw new Error( "jQuery requires a window with a document" );
19                }
20                return factory( w );
21            };
22    } else {
23        factory( global );
24    }
25
26// Pass this if window is not defined yet
27} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
28
29    //这里编写jquery主体代码...
30
31    // AMD
32    if ( typeof define === "function" && define.amd ) {
33        define( "jquery", [], function() {
34            return jQuery;
35        } );
36    }
37
38    var
39        // Map over jQuery in case of overwrite
40        _jQuery = window.jQuery,
41
42        // Map over the $ in case of overwrite
43        _$ = window.$;
44
45    jQuery.noConflict = function( deep ) {
46        if ( window.$ === jQuery ) {
47            window.$ = _$;
48        }
49
50        if ( deep && window.jQuery === jQuery ) {
51            window.jQuery = _jQuery;
52        }
53
54        return jQuery;
55    };
56
57    if ( !noGlobal ) {
58        window.jQuery = window.$ = jQuery;
59    }
60
61    return jQuery;
62} );

这个自执行匿名函数需要传入globalfactory两个形参,分别指全局变量和一个工厂函数。

在这个匿名函数的函数体中对当前所处环境进行判断:

  • 如果所处为支持CommonJS的环境中时,如有window属性和document属性存在,则通过module.exports暴露出工厂函数并可取得jQuery对象以供使用;否则仅暴露出给定抛出错误的工厂函数,比如Nodejs环境
  • 非第一种情况时,则执行匿名函数体中的factory( global ),并在工厂函数中进行AMD的判断、命名冲突检测和全局暴露等操作。

想到这里,需要先停下,我需要重新回顾下CommonJSAMDCMD模块规范,扎实下知识点,另外再借此机会复习ES6中新增的模块规范部分。

一、CommonJS

CommonJS的诞生是由于早先原生js没有模块系统而出现的,它可以在任何地方运行,不只是浏览器,还可以在服务端,其最有名的应用实现就是NodeJS

CommonJS的模块规范:一个文件就是一个模块,每个模块都拥有单独的作用域。普通方式定义的变量、函数、对象都属于该模块内。

  • 通过exportsmodule.exports来暴露模块中的内容。
  • 通过require来加载模块。

使用 exports 暴露模块接口:

代码语言:javascript
复制
 1//studygd.js
 2var hello = function () {
 3    console.log('hello studygd.com.');
 4}
 5exports.hello = hello;
 6
 7//main.js
 8const studygd = require('./studygd');
 9studygd.hello();
10
11//打印:
12//hello studygd.com.

使用 modul.exports 暴露模块对象:

代码语言:javascript
复制
 1//studygd2.js
 2//模块内的私有变量,没有被暴露
 3let text = 10;
 4//公开方法
 5function Study(){
 6    let name;
 7}
 8Study.prototype.setName = function (myName) {
 9    this.name = myName;
10}
11Study.prototype.hello = function () {
12    console.log(`Hello `+ this.name);
13}
14
15module.exports = Study;
16
17//main2.js
18let Study = require('./studygd2');
19let study = new Study();
20study.setName('nitx');
21study.hello();
22
23//打印:
24//Hello nitx

那么exportsmodule.exports有什么区别么?

  • module.exports 初始值为一个空对象 {}
  • exports 是指向的 module.exports 的引用
  • require() 返回的是 module.exports 而不是 exports

二、AMD

AMD全称是Asynchromous Module Definition,即异步模块定义。它的最有名的实现是RequireJS,它是一个浏览器端模块开发的规范。AMD 模式可以用于浏览器环境并且允许非同步加载模块,也可以按需动态加载模块。

AMD模块规范:

  • 通过异步加载模块,模块加载不是影响后面语句的运行,所有依赖某些模块的语句块放置在回调函数中。
  • AMD 规范只定义了一个函数 define,通过 define 方法定义模块。
  • AMD 规范允许输出模块兼容 CommonJS 规范。

在实际使用中,页面需首先加载require.js即:<script type="text/javascript" src="require.js"></script>,才可使用,具体示例可以看文档。

三、CMD

CMD 全称为 Common Module Definition,它的最著名实践是SeaJS

CMDAMD相近,区别如下:

  • 对于依赖的模块 CMD 是延迟执行,而 AMD 是提前执行(不过 RequireJS 从 2.0 开始,也改成可以延迟执行。 )
  • CMD 推崇依赖就近,AMD 推崇依赖前置
  • AMDapi 默认是一个当多个用,CMD 严格的区分推崇职责单一,其每个 API 都简单纯粹

四、ES6的模块规范

ES6的模块规范如下:

  • 一个模块就是一个独立的文件。该文件内部的所有变量,外部无法获取,如果如果你希望外部能够读取模块内部的某个变量,就必须使用export关键字输出该变量
  • export 命令用于规定模块的对外接口,通常情况下,export输出的变量就是本来的名字,但是可以使用as关键字重命名
  • import 命令用于输入其他模块提供的功能
  • ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量

模块功能主要有两个命令构成:exportimportexport命令用于规定模块的对外接口。import命令用于输入其他模块提供的功能。

代码语言:javascript
复制
 1//export.js
 2let firstName = 'Ni';
 3let lastName = 'tx';
 4let year = 30;
 5function fn(){
 6    console.log(2234);
 7}
 8export {
 9        firstName, 
10        lastName, 
11        year,
12        fn as clgFn
13    };
14
15//import.js
16import {firstName} from '../exportPack/e1';
17console.log(firstName);
18
19//打印:
20//Ni

这里有个注意点,现在浏览器和nodejs对于es6模块的支持依然不完全,所以在实际使用中最好通过babel兼容下。

不过未来是ES6模块规范的,这里引述阮一峰的ES6一书一段话:

在 ES6 之前,社区制定了一些模块加载方案,最主要的有 CommonJS 和 AMD 两种。前者用于服务器,后者用于浏览器。ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些东西。比如,CommonJS 模块就是对象,输入时必须查找对象属性。由于 ES6 模块是编译时加载,使得静态分析成为可能。有了它,就能进一步拓宽 JavaScript 的语法,比如引入宏(macro)和类型检验(type system)这些只能靠静态分析实现的功能。除了静态加载带来的各种好处,ES6 模块还有以下好处:不再需要UMD模块格式了,将来服务器和浏览器都会支持 ES6 模块格式。目前,通过各种工具库,其实已经做到了这一点;将来浏览器的新 API 就能用模块格式提供,不再必须做成全局变量或者navigator对象的属性;不再需要对象作为命名空间(比如Math对象),未来这些功能可以通过模块提供。

除了指定加载某个输出值,还可以使用整体加载,即用星号(*)指定一个对象,所有输出值都加载在这个对象上面。

好,以上就是现有的JS模块加载回顾,总结就是ES6模块是现在和未来,在VueReact等框架配合webpack进行项目构建时,可成熟使用,但在jquery等较老库中时,尚未可用,以后也基本不会多支持。而CommonJSAMD等模块规范倒是向下兼容的更好,jQuery中兼容好用,其中CommonJS多用于服务端,而AMD则用于浏览器端,其中代表性实现是RequireJS

所以现在在技术选型选择模块规范时,如用到jQuery,则搭配RequireJS使用;如用到Vue+Webpack,则使用ES6模块。

好,以上就是今天的jQuery源码研究开篇,仅仅只是看了个头,就引出模块规范这个大知识点,脑子里知道和真正写出来的差别还是挺大的,在以后的源码研究中,涉及到的知识点,我都会延伸,熟悉的就回顾,不熟的学习。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2018-12-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端小二 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、CommonJS
  • 二、AMD
  • 三、CMD
  • 四、ES6的模块规范
相关产品与服务
命令行工具
腾讯云命令行工具 TCCLI 是管理腾讯云资源的统一工具。使用腾讯云命令行工具,您可以快速调用腾讯云 API 来管理您的腾讯云资源。此外,您还可以基于腾讯云的命令行工具来做自动化和脚本处理,以更多样的方式进行组合和重用。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档