您的位置:首页 > Web前端 > JQuery

jQuery源代码自我理解(一)

2016-06-23 22:59 429 查看
自己看的源代码,可能理解的不够透彻,但是自己看过一遍感觉还是比较不一样的。希望各路大神多多指教!

下面是自己写的其他源码理解文章:

jQuery源代码自我理解(二)

前面的if判断是否存在commonJs运行环境,若没有,直接执行else的内容。

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 );
}


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 );
}


pushStack()函数用来指定节点链表中该节点的上一个元素,方便元素的查找。

pushStack: function( elems ) {

// Build a new jQuery matched element set
var ret = jQuery.merge( this.constructor(), elems );

// Add the old object onto the stack (as a reference)
ret.prevObject = this;

// Return the newly-formed element set
return ret;
}


其中涉及到的merge()函数是将第二个参数的值复制到第一个参数,然后返回第一个参数:

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;
}


each()函数对数组或对象进行遍历,执行回调函数,如果有一个元素返回false则跳出循环,直到都返回true。

each: function( obj, callback ) {
var length, i = 0;

if ( isArrayLike( obj ) ) {
length = obj.length;
for ( ; i < length; i++ ) {
if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {
break;
}
}
} else {
for ( i in obj ) {
if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {
break;
}
}
}

return obj;
}


each()函数调用了isArrayLike()来判断一个对象是不是类数组类型,其中调用了jQuery.type()函数来判断对象类型。

函数中先取得obj的长度,如果是对象,则
”length” in obj
返回的是false。

type()函数返回obj的类型。

之后

return type === "array" || length === 0 ||
typeof length === "number" && length > 0 && ( length - 1 ) in obj;


如果obj的type为array或长度等于0或者(长度类型为number且长度大于0且长度减一属于obj)则返回true。

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 = jQuery.type( obj );

if ( type === "function" || jQuery.isWindow( obj ) ) {
return false;
}

return type === "array" || length === 0 || typeof length === "number" && length > 0 && ( length - 1 ) in obj;
}


上一个函数使用到的type方法返回对象的类型,其中调用class2type对象的值。

其中一个较长逻辑为判断obj是否为对象或者函数,是的话则调用class2type对象的属性值,否则直接返回obj的类型。

toString.call( obj )返回obj的类型字符串,包括:
"Boolean Number String Function Array Date RegExp Object Error Symbol"
等类型。

class2type[ toString.call( obj ) ]
返回对象中的对应属性值。

type: function( 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;
}


定义class2type对象的属性值,值为一些常见的数据类型,例如布尔类型,数值类型,字符串类型等。

先用split()函数将字符串分割成数组,然后在jQuery.each遍历函数里将数组的值添加到class2type对象里。

jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ),
function( i, name ) {
class2type[ "[object " + name + "]" ] = name.toLowerCase();
} );


Map()函数是对数组进行处理并且返回一个新数组。方法源码类似于each()函数,区别在于map()函数返回一个新数组。

map: function( elems, callback, arg ) {
var length, value,
i = 0,
ret = [];

// Go through the array, translating each of the items to their new values
if ( isArrayLike( elems ) ) {
length = elems.length;
for ( ; i < length; i++ ) {
value = callback( elems[ i ], i, arg );

if ( value != null ) {
ret.push( value );
}
}

// Go through every key on the object,
} else {
for ( i in elems ) {
value = callback( elems[ i ], i, arg );

if ( value != null ) {
ret.push( value );
}
}
}

// Flatten any nested arrays
return concat.apply( [], ret );
}


代码最后一行对数组进行扁平化处理,但是对于嵌套数组而言,map()并不会对其进行判断处理。比如:

var a=[2,[1,3],4,[3,[4,5,32],3],2];
var b=jQuery.map(a,function(v,i){
return v++;
});
console.log(b);


输出的为:
[2, NaN, 4, NaN, 2]


此外,
concat.apply([],ret);
也只会处理一次数组,例如:

var a=[2,[1,3],4,[3,[4,5,32],3],2];
console.log(Array.prototype.concat.apply( [], a ));


输出的结果为:
[2, 1, 3, 4, 3, Array[3], 3, 2]


slice()函数用来取数组的元素,代码中
slice.apply( this, arguments )
冒充js原生的slice()函数,然后保留链式关系。

slice: function() {
return this.pushStack( slice.apply( this, arguments ) );
}


eq()函数选择数组中的第i个元素。其中

j = +i + ( i < 0 ? len : 0 );
表示若参数为负数,则返回(长度+i),否则返回i。

另外,代码
j >= 0 && j < len ? [ this[ j ] ] : []
表示在j>=0且j不超过最大长度时表达式等于数组中下标为j的元素,否则表达式等于空数组。

之后再调用pushStack()函数,确定链式关系。

eq: function( i ) {
var len = this.length,
j = +i + ( i < 0 ? len : 0 );
return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] );
}


end()函数返回元素的上一个节点,或返回该对象的构造函数。

end: function() {
return this.prevObject || this.constructor();
}


jQuery.extend()和jQuery.fn.extend()是实现对象拓展的函数,函数可以传递多个参数,但一般默认为
jQuery.extend([deep], target, object1, [objectN])


其中deep为一个Boolean值,表示是否深度复制对象;

target表示函数调用后接收拓展内容的对象;

object1和object2表示要拓展到target的对象。

函数开始先对一些变量赋值,然后判断传进来的第一个参数是否为布尔值,若是则将变量deep赋值为true,表示深度拓展对象。然后将target变量迁移到下一个参数。

jQuery.extend = jQuery.fn.extend = function() {
var options, name, src, copy, copyIsArray, clone,
target = arguments[ 0 ] || {},
i = 1,
length = arguments.length,
deep = false;

// Handle a deep copy situation
if ( typeof target === "boolean" ) {
deep = target;

// Skip the boolean and the target
target = arguments[ i ] || {};
i++;
}


判断第二个参数,若它既不是对象也不是函数,则令target赋值为空对象。

如果i===length,则表示传递的参数只有一个,此时target则赋值为调用该方法的对象,然后将i自减一。

// Handle case when target is a string or something (possible in deep copy)
if ( typeof target !== "object" && !jQuery.isFunction( target ) ) {
target = {};
}

// Extend jQuery itself if only one argument is passed
if ( i === length ) {
target = this;
i--;
}


之后进行if代码片段(若只有一个参数,前面的代码段执行后i自减一,此时i

for ( ; i < length; i++ ) {

// Only deal with non-null/undefined values
if ( ( options = arguments[ i ] ) != null ) {

// Extend the base object
for ( name in options ) {
src = target[ name ];
copy = options[ name ];

// Prevent never-ending loop
if ( target === copy ) {
continue;
}


如果是深层克隆且copy属性存在且(copy为普通对象或copy为数组)则执行下面的代码,否则直接将copy赋值给target对象。

if ( copyIsArray ) {
copyIsArray = false;
clone = src && jQuery.isArray( src ) ? src : [];
}


copy为数组时执行这段代码,其中
src && jQuery.isArray( src ) ? src : [];
表示src若存在且是数组时,表达式返回src的值,否则重新定义一个新数组。

else {
clone = src && jQuery.isPlainObject( src ) ? src : {};
}


copy若是对象执行这段代码,其中
src && jQuery.isPlainObject( src ) ? src : {};
与上面类似,若src若存在且是对象时,表达式直接返回src的值,否则重新定义一个新对象。

// Recurse if we're merging plain objects or arrays
if ( deep && copy && ( jQuery.isPlainObject( copy ) ||
( copyIsArray = jQuery.isArray( copy ) ) ) ) {

if ( copyIsArray ) {
copyIsArray = false;
clone = src && jQuery.isArray( src ) ? src : [];

} else { clone = src && jQuery.isPlainObject( src ) ? src : {}; }

// Never move original objects, clone them
target[ name ] = jQuery.extend( deep, clone, copy );

// Don't bring in undefined values
} else if ( copy !== undefined ) {
target[ name ] = copy;
}
}
}
}

// Return the modified object
return target;
};


该方法会覆盖原对象的一些属性和方法。

上面用到的isPlainObject()方法是判断对象是否为普通对象。

isPlainObject: function( obj ) {
var proto, Ctor;

// Detect obvious negatives
// Use toString instead of jQuery.type to catch host objects
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;
}


isEmptyObject()是判断对象是否为空,对于数组一样适用

isEmptyObject: function( obj ) {
var name;
for ( name in obj ) {
return false;
}
return true;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: