一、前言
这里主要是为了把学到的原型知识写在这里,所以并不是真正解读jQuery里面的源码,而是把它简化了一下,然后着重讲解里面jQuery的$.fn,$.fn.extend与$.extend。
一、使用立即执行函数定义jQuery
(function(window) {
var
version = "3.3.1",
// 从这里开始定义一个jQuery的变量
jQuery = function( selector, context ) {
// jQuery.fn.init其实是一个构造函数
// 它返回了jQuery的一个实例对象
return new jQuery.fn.init( selector, context );
},
...
window.$ = jQuery;
})(window);
在jQuery源码找到这一段代码,其实它就是使用了立即执行函数,使得对外只暴露$这个变量,防止了造成变量的污染。
然后它使用了jQuery.fn.init这个函数构造了一个jQuery实例对象,那我们接下来看一下这个函数里面是怎么创建对象的。
二、jQuery.fn.init
// 定义jQuery的构造函数
var init = jQuery.fn.init = function (selector) {
var slice = Array.prototype.slice;
// 获取到DOM对象
var dom = slice.call(document.querySelectorAll(selector));
var i, len = dom ? dom.length : 0;
for(i = 0; i < len; i++) {
this[i] = dom[i];
}
this.length = len;
this.selector = selector || '';
};
从上面这部分代码可以看出,jQuery的构造函数是通过querySelectorAll这个函数获取到选中的DOM对象数组(就像我们平常使用$('p'),这样就得到了页面中所有p标签的DOM对象),并且使用length和selector属性分别记下该元素的个数以及它的选择器名。
三、jQuery的原型
我们都知道,js中的原型可以方便我们创建每一个实例后都可以共享同一个方法,这里jQuery也不例外。jQuery也有它的原型。我们来看看jQuery的原型代码实现。
// 定义jQuery的原型
jQuery.fn = jQuery.prototype = {
// jQuery原型的constructor属性指向jQuery这个构造函数本身
constructor: jQuery,
// 定义了jQuery的一些公共方法
toArray: function() {
return slice.call( this );
},
// 这个就是我们常用的$('p').css('background', 'blue')
css: function(key, value) {},
// 这个就是我们常用的$('div').html('我爱js')
html: function(value) {}
...
};
// 还记得上面第二点中说到jQuery构造函数吗
// 那里有一段代码是这样子的
// var init = jQuery.fn.init = function () {...};
// 所以下面这个init其实就是jQuery本身,所以init.prototype的原型指向了jQuery.fn这个对象
init.prototype = jQuery.fn;
我们可以看到,我们平常用到了的css,html等方法,其实就是放置在这个jQuery的原型对象当中。所以我们每次通过$("选择器“”)创建一个实例对象时,它都已经拥有了css,html这些方法。
大家有没想过,为什么不像这样直接把包含了一个公共方法的对象赋值给jQuery的原型呢?
// 为什么不像这样直接把包含了一个公共方法的对象赋值给jQuery的原型呢?
init.prototype = {
constructor: jQuery,
toArray: function() {
return slice.call( this );
},
css: function(key, value) {},
html: function(value) {}
...
};
其实这是因为,jQuery源码只会暴露$这个变量出来给我们用,但是我们有可能需要根据实际的开发需求去扩展jQuery的原型,那么这个时候我们只需要使用$.fn.(自己写的扩展方法名)就可以写进去jQuery的原型对象中了。
并且以后构建一个jQuery对象,这个对象都可以使用我们自定义的这个方法。比如:
是不是看起来特别简单呢?
四、$.fn.extend和$.extend
最后,我们来看看$.fn.extend和$.extend这两个扩展插件机制的函数。
看字面的意思,$.fn.extend就是扩展$.fn(即对jQuery的原型进行扩展),而$.extend就是扩展jQuery(extend是扩展的意思嘛~)。(这里我感觉$.fn.extend({ getName: function(){...} })和上面的$.fn.getName = function() {...}是一个意思)
那么我们来看看,$.fn.extend和$.extend的代码实现吧。(这里我只举例传入一个参数的时候的场景,如果传入多个参数的话,那么是进行合并,而不是扩展插件了)
jQuery.extend = jQuery.fn.extend = function() {
var options, name, src, copy, copyIsArray, clone,
// 把传进来的第一个参数获取到并赋值给target这个变量
target = arguments[ 0 ] || {},
i = 1,
length = arguments.length,
deep = false;
// 省略部分代码
...
// 上面定义了i = 1
// 此时如果参数个数length也是等于1
// 则使得把this赋值给target
// 这里的this,如果是$.fn.extend则this是指$.fn(jQuery的原型)
// 如果是$.extend则this是指$(即jQuery)
if ( i === length ) {
target = this;
i--;
}
for ( ; i < length; i++ ) {
// 由于只传入了一个参数,所以arguments[0]赋值给了options
if ( ( options = arguments[ i ] ) != null ) {
for ( name in options ) {
src = target[ name ];
// copy就是你自己写的扩展方法
copy = options[ name ];
if ( deep && copy && ( jQuery.isPlainObject( copy ) ||
( copyIsArray = Array.isArray( copy ) ) ) ) {
// 省略部分代码
...
} else if ( copy !== undefined ) {
// 把你自己写的扩展方法赋值给target(即$或者$.fn)
target[ name ] = copy;
}
}
}
}
// 返回$或者$.fn
return target;
};
这样,我们就可以通过$.extend和$.fn.extend扩展我们的插件了。
比如,我们经常用的$.ajax就是通过$.extend来扩展出来的。
五、总结
本次懒得总结,我是看视频之后学到的,顺便看了一下jQuery源码,不过现在很少人用jQuery了,我们公司都已经使用vue开发,而现在,我要继续去开发了,如果各位大神觉得上面有什么错误或者建议,欢迎指出~