jQuery1.11源碼分析(7)-----jQuery一些基本的API

這篇文章比較繁雜,主要就是把jQuery源碼從上到下列出來,看個人註釋就行了。javascript

jQuery源碼對各類加載器作了處理。html

//閱讀這個源碼是請先了解一下概念,即時函數,工廠模式
(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 ) {

上面最後的匿名函數至關於初始化函數,下面是該初始化函數的內容java

// 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 = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/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;
}

相關文章
相關標籤/搜索