最近打算学习各种前端框架的写法,吸取经验充实自己,第一个选中的就是JQuery。
JQuery作为最流行的前端框架,早有无数前辈高人写了各种各样的源码解读和剖析,不过他人的东西终究是他人的东西,只有自己去阅读体会到的东西才能确实变成经验,又说好记性不如烂笔头,因此我把自己的学习体会写在这里,方便以后复习。
这篇文章使用的JQuery是3.3.1版本,没有特别的原因,当前是最新版而已。今后的文章也会基于这个版本。
进入正题。
JQuery的主体代码如下:
( 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 ) {
//这里是整个JQ功能区域
});
可以看出,JQ的主体是一个
匿名函数。
那么这里为什么要用匿名函数呢?我想应该是因为匿名函数内部声明的变量不会影响到外部,避免了我们自己声明的一些变量跟JQ起了冲突,导致各种莫名其妙的bug。
把这个匿名函数简化一下,就是如下的形式:
(function(global, factory){
//这里是为了兼容CommonJS模式的一些框架
})(参数1,参数2);
兼容其他框架的写法暂且不表。参数1就是获取window,参数2就是JQ的功能函数。
当不需要兼容其他框架时,下面这条语句才得以执行。
window.jQuery = window.$ = jQuery;
这条语句是把$(即jQuery)设置为全局变量,方便我们使用。
jQuey本身指向了一个函数:
// Define a local copy of jQuery
jQuery = function( selector, context ) {
// The jQuery object is actually just the init constructor 'enhanced'
// Need init if jQuery is called (just allow error to be thrown if not included)
return new jQuery.fn.init( selector, context );
},
可以看到我们每次使用$,其实都是new了一个实例出来,只不过这部分工作JQ替我们做了,不需要再自己创建。
令我百思不得其解的是,new一个实例的时候用的是jQuery.fn.init,看了源码JQ是通过jQuery.fn = jQuery.prototype来使jQuery.fn直接指向jQuery.prototype的。直接使用jQuery.prototype不就好了,何必多此一举使用jQuery.fn呢?