前端必备:熟读框架源码,今天来分析
jQuery
的源码css
咱们想要读一个框架的源码,那第一步确定是下载源码(这是废话)。前端
地址:jQuery.js(本文代码版本为jquery3.4.1
)jquery
点击上面地址,将代码粘贴到本身的本地编辑器中,而后开始进行下一步工做。git
这一步,咱们分析代码,研究代码结构es6
咱们经过观察源码的结构,将代码分割成了三个部分github
/*
* 第一部分:匿名函数结构
*/
/*!
* jQuery JavaScript Library v3.4.1
* https://jquery.com/
*
* Includes Sizzle.js
* https://sizzlejs.com/
*
* Copyright JS Foundation and other contributors
* Released under the MIT license
* https://jquery.org/license
*
* Date: 2019-05-01T21:04Z
*/
( function( global, factory ) {
"use strict";
if ( typeof module === "object" && typeof module.exports === "object" ) {
// For CommonJS and CommonJS-like environments where a proper `window`
// is present, execute the factory and get jQuery.
// For environments that do not have a `window` with a `document`
// (such as Node.js), expose a factory as module.exports.
// This accentuates the need for the creation of a real `window`.
// e.g. var jQuery = require("jquery")(window);
// See ticket #14549 for more info.
module.exports = global.document ?
factory( global, true ) :
function( w ) {
if ( !w.document ) {
throw new Error( "jQuery requires a window with a document" );
}
return factory( w );
};
} else {
factory( global );
}
// Pass this if window is not defined yet
} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
/*
* 第二部分:功能代码(省略)
*/
// 第三部分:返回
// Register as a named AMD module, since jQuery can be concatenated with other
// files that may use define, but not via a proper concatenation script that
// understands anonymous AMD modules. A named AMD is safest and most robust
// way to register. Lowercase jquery is used because AMD module names are
// derived from file names, and jQuery is normally delivered in a lowercase
// file name. Do this after creating the global so that if an AMD module wants
// to call noConflict to hide this version of jQuery, it will work.
// Note that for maximum portability, libraries that are not jQuery should
// declare themselves as anonymous modules, and avoid setting a global if an
// AMD loader is present. jQuery is a special case. For more information, see
// https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon
if ( typeof define === "function" && define.amd ) {
define( "jquery", [], function() {
return jQuery;
} );
}
var
// Map over jQuery in case of overwrite
_jQuery = window.jQuery,
// Map over the $ in case of overwrite
_$ = window.$;
jQuery.noConflict = function( deep ) {
if ( window.$ === jQuery ) {
window.$ = _$;
}
if ( deep && window.jQuery === jQuery ) {
window.jQuery = _jQuery;
}
return jQuery;
};
// Expose jQuery and $ identifiers, even in AMD
// (#7102#comment:10, https://github.com/jquery/jquery/pull/557)
// and CommonJS for browser emulators (#13566)
if ( !noGlobal ) {
window.jQuery = window.$ = jQuery;
}
return jQuery;
});
复制代码
首先介绍一下匿名函数:编程
①匿名函数的结构json
/*
* 第一个括号内为匿名函数
* 第二个括号用于调用函数,并传参
*/
(function(x,y){
})(x,y)
复制代码
②匿名函数的做用浏览器
for(var i = 0;i <= 10;i++) {
setTimeout(() => {
console.log(i);
}, 1000)
}
复制代码
咱们执行上面这一段代码,结果是输出了11
个11
,以下图:bash
for
循环执行的速度要比
setTimeout
快得多,因此当跳出for循环时才能够执行到
setTimeout
里面的函数中(
console.log(i)
),因此执行上述代码片断,输出的是咱们所看到的结果。
因此,如今咱们想输出1-10,那咱们应该怎么作?
通常有两种方式:
I、一种是用es6
中的声明变量关键字let
代替var
,咱们来看一下效果
for(var i = 0;i <= 10;i++) {
(function(i){
setTimeout(function(){
console.log(i);
}, 1000)
})(i)
}
复制代码
在这个方法中,咱们使用了匿名函数,匿名函数传入参数i
,并接收,这个时候也获得了咱们想要的0-10
的结果,以下图
①匿名函数能够自动执行
②提供模块化,将变量传入,为全局变量提供局部范围,能够减小查找时的开销量等。
介绍完上述基本概念,下面进行代码解析
1、第一部分
1.1 先来看第一大段注释部分
这么一段很长的注释包括了:jQuery
库的版本以及地址、包含的库Sizzle.js
及官网地址,其中这个Sizzle.js
是一个纯JavaScript
css
选择器引擎、版权说明、以及发布日期
1.2 接下来看匿名函数部分
1.2.1 它传入了两个参数并接收
typeof window !== "undefined" ? window : this
这里对浏览器是否支持window
环境作了一个判断,假如不支持,则传入this
对象
function( window, noGlobal ) {}
这里传入了一个函数,window
和noGloabl
做为参数,其实这个函数就是jQuery
的功能函数部分
同时接收了上面两个参数(global
对象和factory
函数)。
1.2.2 "use strict"
1.2.3 咱们看下面这一段代码,if-else
语句中判断了module
的类型,其实这段代码的含义,就是咱们在使用jQuery
框架的时候,是用了哪一种方式引入:
if ( typeof module === "object" && typeof module.exports === "object" ) {
// 先省略
} else {
factory( global );
}
复制代码
<script src="jQuery.js"></script>
这个时候,是检测不到module
的,也就是module
的值为undefined
,那么这个时候代码天然而然的就执行了else
语句,也就是直接调用了factory
函数,并在函数中传入this
或是Windows
对象。
require
引入jQuery
const jquery = require('jquery');
复制代码
咱们能够尝试输出一下module
,获得了这样的结果
module
再也不是
undefined
,而是一个
json
类型的数据,这个时候也就执行了
if-else
语句中的
if
条件下的语句
1.2.4 接下来,咱们看if
条件下到底写了些什么?
// For CommonJS and CommonJS-like environments where a proper `window`
// is present, execute the factory and get jQuery.
// For environments that do not have a `window` with a `document`
// (such as Node.js), expose a factory as module.exports.
// This accentuates the need for the creation of a real `window`.
// e.g. var jQuery = require("jquery")(window);
// See ticket #14549 for more info.
module.exports = global.document ?
factory( global, true ) :
function( w ) {
if ( !w.document ) {
throw new Error( "jQuery requires a window with a document" );
}
return factory( w );
};
复制代码
//对于CommonJS和相似于CommonJS的环境,其中有一个适当的window
//当前,执行factory并得到jQuery。
//对于没有带有document的window的环境
//(例如Node.js),将工厂公开为module.exports。
//这强调了建立一个真正的window的必要性。
//例如var jQuery = require("jQuery")(window);
复制代码
这里会判断global里面有没有document属性,若是有,就会执行factory函数,若是没有就会抛出一个异常(jQuery requires a window with a document
[jQuery
须要一个带有document
的window
]),这个时候一般是所处的浏览器环境不支持使用jQuery
框架。
2、第三部分
咱们仔细观察这一部分,就会发现:这一部分其实就是处理向外暴露jQuery和$标识符,代码在这一部分判断了不少种状况,这里的处理便于大众使用jQuery
框架。
3、第二部分
这一部分是功能代码部分,大概有一万多行的代码,看这一部分的代码,要学会人家的编程思想以及编写一个框架的思惟方式
PS: 建议像我这样没有把jQuery API
全记下来的群体,能够对照着API
文档看源码,这样可以更好的理解、了解功能的实现过程,也可以更好的记住这些API
。
咱们经过上述的步骤,分析了jQuery源码,了解了一个框架的基本结构,也知道了它为何会这样写框架。咱们也就能够经过上面的方法,不妨能够尝试写出来一个属于本身的库~
对于仍是一个小白的我来说,写出来的文章会有很差、甚至错误的地方,还但愿你们积极指正~让小白快速成长~