学习前端也有一段时间了,闲暇之余决定进一步学习,从jquery源码学习开始。
嗯,打开jquery.js,嗯,太多了,一脸懵!
一步一步来吧!
刚开始看了艾伦 Aaron老师的文章入门引导一下。
首先是看jQuery源码分析系列(01) : 整体架构这篇。
提前声明:后文所展示的jquery版本为3.3.1,后文不再强调,理解不对的地方万望告知
总结一下:
ps:文中写到了jQuery.fn但是并没有写怎么来的,搞得是我一头懵.于是去jquery.js文件中搜索一下,可以看到有这么一段
很明显Jquery.fn就是jQuery.prototype。
一、如何做到既能隔离作用域还能使用jQuery原型对象的作用域呢?
var aQuery = function(selector, context) {
return new aQuery.prototype.init(selector);
}
aQuery.prototype = {
init: function(selector) {
//此处的this指的时init方法的实例,即new aQuery.prototype.init(selector)
this.name = selector;
return this;
},
name: function() {
return this.age
},
age: 20
}
//将aQuery原型对象上的init方法的原型对象设置为aQuery的原型对象。
//这样就init的实例就可以访问到aQuery原型对象上的方法.
aQuery.prototype.init.prototype = aQuery.prototype;
var a = aQuery(1);
var b = aQuery(2);
console.log(a.name,a.age)//1,20
console.log(b.name,b.age)//2,20
二、插件接口,extend方法
推荐这篇文章 分析了一下JQuery中的extend方法实现原理,很详细的介绍了extend的用法.
extend方法中使用了isPlainObject方法。
源码解析如下
/*
jQuery.fn === jQuery.prototype;
在jquery对象上和jquery.prototype上增加一个extend方法
什么是目标对象和源对象?
将obj2合并到obj1,则obj1是目标对象,obj2是源对象
*/
jQuery.extend = jQuery.fn.extend = function() {
var options, name, src, copy, copyIsArray, clone,
target = arguments[ 0 ] || {},//目标对象
i = 1,//记录源对象在实参中的开始位置
length = arguments.length,//实参长度
deep = false;//是不是深拷贝
// 判断是一个参数是true的时候为深拷贝
if ( typeof target === "boolean" ) {
//是深拷贝
deep = target;
// 修改目标对象为第二个实参.
target = arguments[ i ] || {};
//源对象在实参中的开始位置为2
i++;
}
//如果传入的目标对象不是一个对象并且不是函数时,则置为空对象.
if ( typeof target !== "object" && !isFunction( target ) ) {
target = {};
}
// 当实参只有一个对象的时候,默认把jQuery对象作为目标对象
if ( i === length ) {
target = this;
i--;
}
//循环源对象
for ( ; i < length; i++ ) {
// 当源对象不为null时
if ( ( options = arguments[ i ] ) != null ) {
// 遍历当前源对象
for ( name in options ) {
src = target[ name ];//目标对象的该属性
copy = options[ name ];//源对象的该属性
// Prevent never-ending loop
if ( target === copy ) {
continue;
}
// 如果是深拷贝,判断copy是对象还是数组,如果既不是纯粹的对象也不是数组则直接走else if
if ( deep && copy && ( jQuery.isPlainObject( copy ) ||
( copyIsArray = Array.isArray( copy ) ) ) ) {
//如果是一个数组
if ( copyIsArray ) {
copyIsArray = false;
//复制目标对象的值
clone = src && Array.isArray( src ) ? src : [];
} else {
//如果是对象
//复制目标对象的值
clone = src && jQuery.isPlainObject( src ) ? src : {};
}
// 递归调用extend方法,将源对象合并到clone上并赋值给目标对象.
target[ name ] = jQuery.extend( deep, clone, copy );
// 浅拷贝则直接将源对象的属性值赋值给目标对象
} else if ( copy !== undefined ) {
target[ name ] = copy;
}
}
}
}
// 返回目标对象.
return target;
};
//判断指定参数是否是一个纯粹的对象,所谓"纯粹的对象",就是该对象是通过"{}"或"new Object()"创建的。
isPlainObject: function( obj ) {
var proto, Ctor;
// 如果参数为假值,或者{}.toString.call(obj)判断不是对象则返回false
if ( !obj || toString.call( obj ) !== "[object Object]" ) {
return false;
}
//Object.getPrototypeOf(obj);
proto = getProto( obj );
//如果是使用Object.create(null)创建的对象也是纯粹的对象.proto值为null
if ( !proto ) {
return true;
}
//直接return Object.getPrototypeOf(obj).constructor === Object效果和下面两句是一样的
// return Object.getPrototypeOf(obj).constructor === Object
//如果obj的原型对象的引用是Object,则是纯粹的对象,即Object.getPrototypeOf(obj).constructor === Object
Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor;
return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString;
},