源码声明makeArray.png本来想看jQuery.makeArray,看到了jQuery.extend,其中扩充了好多方法属性,看来要优先一下。
jQuery.extend
jQuery.extend = jQuery.fn.extend = function() {
var options, name, src, copy, copyIsArray, clone,
target = arguments[ 0 ] || {}, // 合并对象默认为空对象
i = 1,
length = arguments.length, // 参数个数
deep = false; // 拷贝类型
if ( typeof target === "boolean" ) {
deep = target;
target = arguments[ i ] || {};
i++;
}
// Handle case when target is a string or something (possible in deep copy)
if ( typeof target !== "object" && !isFunction( target ) ) {
target = {};
}
// Extend jQuery itself if only one argument is passed
if ( i === length ) {
target = this;
i--;
}
for ( ; i < length; i++ ) {
// do arguments[i]
}
return target;
};
isFunction
- target是布尔值:作为拷贝类型,target到下一个参数。
- target非对象非函数:作为拷贝类型,target到下一个参数。
- i越界,i--,target赋为调用对象。
- 遍历剩余参数,返回target。
遍历的时候做了什么呢?
for ( ; i < length; i++ ) {
if ( ( options = arguments[ i ] ) != null ) {
for ( name in options ) {
src = target[ name ];
copy = options[ name ];
if ( target === copy ) {
continue;
}
// Recurse if we're merging plain objects or arrays
if ( deep && copy && ( jQuery.isPlainObject( copy ) ||
( copyIsArray = Array.isArray( copy ) ) ) ) {
// 深拷贝
} else if ( copy !== undefined ) {
target[ name ] = copy;
}
}
}
}
注意这一段if ( target === copy ) {continue;}当容器和要拷贝的项是相同引用时,就跳过这一项。浅拷贝的操作就是将拷贝项对到容器的属性中。其中有一段引人注目赋值语句的返回值就是语句的右值。
深拷贝段
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 : {};
}
// Never move original objects, clone them
target[ name ] = jQuery.extend( deep, clone, copy );
}
对于冲突属性,递归将这两个属性传入extend。
JQ的合并对象竟然是用递归实现的,非递归的手动压栈应该性能更棒!
isFunction
var isFunction = function isFunction( obj ) {
// 判断参数是不是函数,避免DOM节点参数的特殊情况。
return typeof obj === "function" && typeof obj.nodeType !== "number";
};
除了直观的typeof,规避一种特殊的情况DOM节点在老IE判为function
现在我们可以做对象合并了,就能聊更多扩充方法了。
jQuery.isPlainObject
jQuery.extend( {
isPlainObject: function( obj ) {
var proto, Ctor;
if ( !obj || toString.call( obj ) !== "[object Object]" ) {
return false;
}
proto = getProto( obj );
// Objects with no prototype (e.g., `Object.create( null )`) are plain
if ( !proto ) {
return true;
}
// Objects with prototype are plain iff
// they were constructed by a global Object function
Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor;
return typeof Ctor === "function" &&
fnToString.call( Ctor ) === ObjectFunctionString;
}
} );
toString getProto hasOwn fnToString ObjectFunctionString
判断是否是基类生成对象或无原型的纯净对象
toString fnToString hasOwn getProto ObjectFunctionString
var class2type = {};
var toString = class2type.toString;
var hasOwn = class2type.hasOwnProperty;
var fnToString = hasOwn.toString;
看到这两个函数都是熟悉的面孔。toString就是Object基类原型的toString,fnToString是函数的toString,hasOwn是一个监测是否是对象本身属性(非原型)的函数。
var ObjectFunctionString = fnToString.call( Object );
ObjectFunctionString 不是别的就是'function Object() { [native code] } '这样一个字符串。
var getProto = Object.getPrototypeOf;
是一个返回参数的原型的函数。
所以jq判断一个对象的纯净考虑的方面:toString是不是Object、有没有原型、原型里的constructor是不是Object()
jQuery.makeArray
jQuery.extend( {
makeArray: function( arr, results ) {
var ret = results || [];
if ( arr != null ) {
if ( isArrayLike( Object( arr ) ) ) {
jQuery.merge( ret,
typeof arr === "string" ?
[ arr ] : arr
);
} else {
push.call( ret, arr );
}
}
return ret;
}
} );
push = arr.push
Object()传入字符串返回的类数组对象
results 可以作为合并类数组的数组容器,非类数组直接调用push函数。
isArrayLike
function isArrayLike( obj ) {
// Support: real iOS 8.2 only (not reproducible in simulator)
// `in` check used to prevent JIT error (gh-2145)
// hasOwn isn't used here due to false negatives
// regarding Nodelist length in IE
var length = !!obj && "length" in obj && obj.length,
type = toType( obj );
if ( isFunction( obj ) || isWindow( obj ) ) {
return false;
}
return type === "array" || length === 0 ||
typeof length === "number" && length > 0 && ( length - 1 ) in obj;
}
又有许多的生面孔。。。。。。一个一个来
toType
主要为了规避null和function的特殊显示
function toType( obj ) {
if ( obj == null ) {
return obj + "";
}
// Support: Android <=2.3 only (functionish RegExp)
return typeof obj === "object" || typeof obj === "function" ?
class2type[ toString.call( obj ) ] || "object" :
typeof obj;
}
isWindow
判断参数是不是window对象
var isWindow = function isWindow( obj ) {
return obj != null && obj === obj.window;
};
现在回顾判断类数组的思路,是不是狭义的对象,length属性是否是自然数,length-1是不是在参数结构内。
jQuery.merge
jQuery.extend( {
merge: function( first, second ) {
var len = +second.length,
j = 0,
i = first.length;
for ( ; j < len; j++ ) {
first[ i++ ] = second[ j ];
}
first.length = i;
return first;
}
} );
如果作为一个数组拼接的方法,原生拼接的方法就可以(concat、push),我想到了jq对象,拼接DOM集合。重要的区别:该方法改变第一参数
至此,前面挖的坑多数都填上了,至于后面的顺序我准备参照某大佬的顺序。
补充一点,第一篇的find(),找到了。
jQuery.find = Sizzle;
引用一下大佬的话:find 函数实际上是 Sizzle,已经单独出来一个项目,被在 jQuery 中直接使用,将在下章介绍 jQuery 中的 Sizzle 选择器。
网友评论