zoukankan      html  css  js  c++  java
  • jQuery1.11源码分析(7)-----jQuery一些基本的API

    这篇文章比较繁杂,主要就是把jQuery源码从上到下列出来,看我的注释就好了。

    jQuery源码对各种加载器做了处理。

    //阅读这个源码是请先了解一下概念,即时函数,工厂模式
    (function( global, factory ) {
        //这里之所以这样处理,是为了考虑CommonJS的环境,所以先检测是否有CommonJS的环境。
    	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 inherently posses a window with a document
    		// (such as Node.js), expose a jQuery-making 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
            //有CommonJS环境则输出一个方法
    		module.exports = global.document ?
                //若全局环境有document,则工厂函数调用时传入第二个参数
    			factory( global, true ) :
                //若全局环境没有document,则返回给CommonJS加载器一个留待以后调用的函数。
    			function( w ) {
    				if ( !w.document ) {
    					throw new Error( "jQuery requires a window with a document" );
    				}
    				return factory( w );
    			};
    	} else {
            //如果没有CommonJS环境,则直接执行函数获得全局jQuery方法。
    		factory( global );
    	}
    // Pass this if window is not defined yet
    //若全局环境里没有window(可能为NodeJS),则调用上下文传入this。
    }(typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
    

    上面最后的匿名函数相当于初始化函数,下面是该初始化函数的内容

    // Can't do this because several apps including ASP.NET trace
    // the stack via arguments.caller.callee and Firefox dies if
    // you try to trace through "use strict" call chains. (#13335)
    // Support: Firefox 18+
    //这里我没看懂,补充链接
    //http://developer.zdnet.com.cn/2007/0212/377947.shtml
    //http://blog.csdn.net/arwindgao/article/details/6592357
    
    //缓存各个方法到当前作用域,因为避免了每次调用slice等函数时要进行更深层次的查找,这样可以提升性能,更多方式可看《高性能Javascript》
    var deletedIds = [];
    
    var slice = deletedIds.slice;
    
    var concat = deletedIds.concat;
    
    var push = deletedIds.push;
    
    var indexOf = deletedIds.indexOf;
    //这个对象会在后面进行初始化,主要用来判断类型
    var class2type = {};
    
    var toString = class2type.toString;
    
    var hasOwn = class2type.hasOwnProperty;
    
    var trim = "".trim;
    //这个后面用来保存支持性信息
    var support = {};
    
    
    
    var
    	version = "1.11.0",
    
    	// Define a local copy of jQuery
        //jQuery函数即是$函数,第一个参数是选择符,第二个参数是使用选择符查找的上下文环境
        // 比如要在父DOM里查找某子元素,$('body',document)
    	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 );
    	},
    
    	// Make sure we trim BOM and NBSP (here's looking at you, Safari 5.0 and IE)
    //此处的BOM是Byte Order Mark的缩写,字节序标记,为uFEFF,必须去除
        //xA0则是latin01编码中的nbsp;
    	rtrim = /^[suFEFFxA0]+|[suFEFFxA0]+$/g,
    
    	// Matches dashed string for camelizing
        //后面这里主要负责把dash格式的变量名(webkit-transform)转换为驼峰格式(webkitTransform)
    	rmsPrefix = /^-ms-/,
    	rdashAlpha = /-([da-z])/gi,
    
    	// Used by jQuery.camelCase as callback to replace()
        //用来真正替换用的回调函数,后面真正用的时候的代码如下
        //camelCase: function( string ) {
        //        return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
        //},
    	fcamelCase = function( all, letter ) {
    		return letter.toUpperCase();
    	};
    //须了解概念,原型和原型链。
    //此处将jQuery工厂函数的原型设置为一个包含制定属性的对象。
    jQuery.fn = jQuery.prototype = {
    	// The current version of jQuery being used
    	jquery: version,
        //覆盖工厂函数的原型时必须保证constructor属性依旧指向原工厂函数
    	constructor: jQuery,
        //最开始选择符为空
    	// Start with an empty selector
    	selector: "",
    
    	// The default length of a jQuery object is 0
    	length: 0,
        //调用数组的slice方法可以把this转换为数组。
        //为什么要用这个方法?添加了自定义属性的jQuery对象还是数组,可以试试Array.isArray($('body'))
    	toArray: function() {
    		return slice.call( this );
    	},
    
    	// Get the Nth element in the matched element set OR
    	// Get the whole matched element set as a clean array
        //传数区间为[-length,length]
        //和后面eq的区别是,get返回DOM元素,eq返回jQuery对象
    	get: function( num ) {
    		return num != null ?
    
    			// Return a 'clean' array
                //如果传入的num为负数,则变为取从队尾开始数第N个元素
    			( num < 0 ? this[ num + this.length ] : this[ num ] ) :
    
    			// Return just the object
                //之所以不调用toArray,是因为同样只需要一行代码,减轻耦合以及函数调用的开销
    			slice.call( this );
    	},
    
    	// Take an array of elements and push it onto the stack
    	// (returning the new matched element set)
        //压栈函数,因为后面由部分工具需要改变 当前插找DOM元素的上下文,所以需要保存当前的环境。
        //后续函数实现依赖于此函数,凡是需要改变当前匹配元素集的操作,都需要进行压栈操作
    	pushStack: function( elems ) {
    
    		// Build a new jQuery matched element set
            //生成新的jQuery对象。
            //为什么要用这种奇葩的方式生成,不能直接$(elems)生成?
    		var ret = jQuery.merge( this.constructor(), elems );
    
    		// Add the old object onto the stack (as a reference)
            //prevObject指向之前的jQuery对象
    		ret.prevObject = this;
    		ret.context = this.context;
    
    		// Return the newly-formed element set
    		return ret;
    	},
    
    	// Execute a callback for every element in the matched set.
    	// (You can seed the arguments with an array of args, but this is
    	// only used internally.)
        //此处必须区分两个函数,jQuery.prototype.each和jQuery.each,
        //前者给由jQuery工厂函数生成的对象来使用,后者是以jQuery工厂函数为命名空间,把工具函数each绑定到jQuery工厂函数上,避免了全局污染。
        //前者通过调用后者来实现。
        //后面的很多函数实现都是采用这种方式。
    	each: function( callback, args ) {
    		return jQuery.each( this, callback, args );
    	},
    
    	map: function( callback ) {
            //因为map会生成新的jQuery对象,所以要pushStack
    		return this.pushStack( jQuery.map(this, function( elem, i ) {
    			return callback.call( elem, i, elem );
    		}));
    	},
    
    	slice: function() {
    		return this.pushStack( slice.apply( this, arguments ) );
    	},
    
    	first: function() {
    		return this.eq( 0 );
    	},
    
    	last: function() {
    		return this.eq( -1 );
    	},
        //eq取第几个元素然后生成jQuery对象
    	eq: function( i ) {
    		var len = this.length,
    			j = +i + ( i < 0 ? len : 0 ); //j = +i < 0 ? +i + len:+i 原写法比较好的是减少一次隐式转换
    		return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] );
    	},
        //对于本次jQuery对象的操作结束,返回压栈前的jQuery对象。
    	end: function() {
    		return this.prevObject || this.constructor(null);
    	},
    
    	// For internal use only.
    	// Behaves like an Array's method, not like a jQuery method.
    	push: push,
    	sort: deletedIds.sort,
    	splice: deletedIds.splice
    };
    //扩展对象函数
    //接受三个参数,是否为深复制(可选),目标对象,源对象。
    //之所以把深复制参数放在最前面,是因为方便增加源对象的个数。
    jQuery.extend = jQuery.fn.extend = function() {
    	var src, copyIsArray, copy, name, options, clone,
    		target = arguments[0] || {},
    		i = 1,
    		length = arguments.length,
    		deep = false;
        //前面这几部分主要是为了复用,可以学习一下这种复用方式
    	// Handle a deep copy situation
        //当传入的第一个参数是bool型
    	if ( typeof target === "boolean" ) {
    		deep = target;
    
    		// skip the boolean and the target
            //跳过深复制标识符,重新获得新的目标对象。
    		target = arguments[ i ] || {};
    		i++;
    	}
    
    	// Handle case when target is a string or something (possible in deep copy)
        //为什么target不能是Function?因为有可能覆盖Function的一些属性
    	if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
    		target = {};
    	}
    
    	// extend jQuery itself if only one argument is passed
        //如果只传进来一个对象,则说明目标对象为本jQuery对象。
    	if ( i === length ) {
    		target = this;
    		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;
    				}
    
    				// Recurse if we're merging plain objects or arrays
                    //如果进行深复制且待复制属性是朴素对象或队列
    				if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
    					//深复制的时候要注意区分待复制的属性是对象还是数组
                        if ( copyIsArray ) {
    						copyIsArray = false;            //使用此变量的目的是为了减少一次isArray的调用
    
    						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;
    };
    //定义完扩展函数,就可以使用它了,添加一堆工具函数
    jQuery.extend({
    	// Unique for each copy of jQuery on the page
    	expando: "jQuery" + ( version + Math.random() ).replace( /D/g, "" ),
    
    	// Assume jQuery is ready without the ready module
    	isReady: true,
    
    	error: function( msg ) {
    		throw new Error( msg );
    	},
    
    	noop: function() {},
    
    	// See test/unit/core.js for details concerning isFunction.
    	// Since version 1.3, DOM methods and functions like alert
    	// aren't supported. They return false on IE (#2968).
        //这个写法是处理之前遗留的bug
    	isFunction: function( obj ) {
    		return jQuery.type(obj) === "function";
    	},
        //先检测是否有原生的isArray方法。
    	isArray: Array.isArray || function( obj ) {
    		return jQuery.type(obj) === "array";
    	},
        //通过检测对象下是否有window属性。。这个可以伪造。。
    	isWindow: function( obj ) {
    		/* jshint eqeqeq: false */
    		return obj != null && obj == obj.window;
    	},
        //是否为数字
    	isNumeric: function( obj ) {
    		// parseFloat NaNs numeric-cast false positives (null|true|false|"")
    		// ...but misinterprets leading-number strings, particularly hex literals ("0x...")
    		// subtraction forces infinities to NaN
            //当我们用typeof 来判断时,NaN和infinity都为number
            //这种方法可以检测字符串是否可以转换为数字。
    		return obj - parseFloat( obj ) >= 0;
    	},
        //检测空对象的方法
    	isEmptyObject: function( obj ) {
    		var name;
    		for ( name in obj ) {
    			return false;
    		}
    		return true;
    	},
        //检测是否为朴素对象,不能为DOM和window,不能为由工厂函数生产出来的对象
        //这里用了多种检测方式,值得学习
    	isPlainObject: function( obj ) {
    		var key;
    
    		// Must be an Object.
    		// Because of IE, we also have to check the presence of the constructor property.
    		// Make sure that DOM nodes and window objects don't pass through, as well
    		if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
    			return false;
    		}
    
    		try {
    			// Not own constructor property must be Object
                //为什么这里要加两个叹号?
    			if ( obj.constructor &&
    				!hasOwn.call(obj, "constructor") &&
    				!hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
    				return false;
    			}
    		} catch ( e ) {
    			// IE8,9 Will throw exceptions on certain host objects #9897
                // In some very specific cases (ex: IE accessing window.location of another window)
    			return false;
    		}
    
    		// Support: IE 0 && ( length - 1 ) in obj;
    }
    
    
  • 相关阅读:
    将地址转换为链接的正则表达式(regex url href)
    [收藏]中国惠普前总裁孙振耀谈人生
    沪江技术团队寻觅新成员,下一位会是你吗?
    来点圣诞气氛
    让VisualSVN Server支持匿名访问
    11月25日博客园南京园友聚会
    [上海俱乐部活动]博文视点与博客园系列图书作者见面会暨.NET技术交流会
    比尔·盖茨在微软的最后一天
    真让人兴奋
    博客园新服务器已下订单
  • 原文地址:https://www.cnblogs.com/suprise/p/3662099.html
Copyright © 2011-2022 走看看