jQuery源代码自我理解(一)
2016-06-23 22:59
429 查看
自己看的源代码,可能理解的不够透彻,但是自己看过一遍感觉还是比较不一样的。希望各路大神多多指教!
下面是自己写的其他源码理解文章:
jQuery源代码自我理解(二)
前面的if判断是否存在commonJs运行环境,若没有,直接执行else的内容。
jQuery选择页面元素的源码,之后学习再深入。
pushStack()函数用来指定节点链表中该节点的上一个元素,方便元素的查找。
其中涉及到的merge()函数是将第二个参数的值复制到第一个参数,然后返回第一个参数:
each()函数对数组或对象进行遍历,执行回调函数,如果有一个元素返回false则跳出循环,直到都返回true。
each()函数调用了isArrayLike()来判断一个对象是不是类数组类型,其中调用了jQuery.type()函数来判断对象类型。
函数中先取得obj的长度,如果是对象,则
type()函数返回obj的类型。
之后
如果obj的type为array或长度等于0或者(长度类型为number且长度大于0且长度减一属于obj)则返回true。
上一个函数使用到的type方法返回对象的类型,其中调用class2type对象的值。
其中一个较长逻辑为判断obj是否为对象或者函数,是的话则调用class2type对象的属性值,否则直接返回obj的类型。
toString.call( obj )返回obj的类型字符串,包括:
定义class2type对象的属性值,值为一些常见的数据类型,例如布尔类型,数值类型,字符串类型等。
先用split()函数将字符串分割成数组,然后在jQuery.each遍历函数里将数组的值添加到class2type对象里。
Map()函数是对数组进行处理并且返回一个新数组。方法源码类似于each()函数,区别在于map()函数返回一个新数组。
代码最后一行对数组进行扁平化处理,但是对于嵌套数组而言,map()并不会对其进行判断处理。比如:
输出的为:
此外,
输出的结果为:
slice()函数用来取数组的元素,代码中
eq()函数选择数组中的第i个元素。其中
另外,代码
之后再调用pushStack()函数,确定链式关系。
end()函数返回元素的上一个节点,或返回该对象的构造函数。
jQuery.extend()和jQuery.fn.extend()是实现对象拓展的函数,函数可以传递多个参数,但一般默认为
其中deep为一个Boolean值,表示是否深度复制对象;
target表示函数调用后接收拓展内容的对象;
object1和object2表示要拓展到target的对象。
函数开始先对一些变量赋值,然后判断传进来的第一个参数是否为布尔值,若是则将变量deep赋值为true,表示深度拓展对象。然后将target变量迁移到下一个参数。
判断第二个参数,若它既不是对象也不是函数,则令target赋值为空对象。
如果i===length,则表示传递的参数只有一个,此时target则赋值为调用该方法的对象,然后将i自减一。
之后进行if代码片段(若只有一个参数,前面的代码段执行后i自减一,此时i
如果是深层克隆且copy属性存在且(copy为普通对象或copy为数组)则执行下面的代码,否则直接将copy赋值给target对象。
copy为数组时执行这段代码,其中
copy若是对象执行这段代码,其中
该方法会覆盖原对象的一些属性和方法。
上面用到的isPlainObject()方法是判断对象是否为普通对象。
isEmptyObject()是判断对象是否为空,对于数组一样适用
下面是自己写的其他源码理解文章:
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; }
相关文章推荐
- jquery中ajax方法load get post与脚本文件如php脚本连接时,脚本怎样接受数据?
- jquery中fadein函数实现图片逐渐清晰显示
- jQuery 各类判断函数汇总
- hwSlider-内容滑动切换效果(三):jquery.hwSlide.js插件封装
- jQuery 中$.browser 替换
- jquery submit()不能提交表单的解决方法
- 推荐几款jquery图片切换插件
- jQuery常用方法一览
- 编写高效jQuery代码
- jquery常用语句总结
- .net中Jquery ajax调用aspx和ashx文件
- jquery ajax 如何设置同步
- jquery tmpl()切换页面加载不出问题的解决
- JQuery DIV 动态隐藏和显示的方法
- jquery.cookie用法详细解析
- jQuery插件扩展extend的实现原理
- JQuery Mobile 应用实例(1)
- jquery . fancybox()
- jquery关于select框的取值和赋值
- <jquery>jquery动态合并表格