天天看點

【jquery源碼五】jQuery工具方法彙總②。

前言:上篇文章已經分析了16個常用工具方法了,現在來繼續分析後面的17個常用工具方法。

【jquery源碼】目錄

【jquery源碼五】jQuery工具方法彙總①。

一、工具方法

jQuery.extend({
	parseHTML: function( data, context, keepScripts ){}, 
	parseXML: function( data ){},	
	noop: function() {},
	globalEval: function( code ){},
	camelCase: function( string ){},	
	nodeName: function( elem, name ){},
	makeArray: function( arr, results ){},
	inArray: function( elem, arr, i ){},	
	merge: function( first, second ){},
	grep: function( elems, callback, inv ){},	
	map: function( elems, callback, arg ){},
	guid: 1,
	proxy: function( fn, context ){},	
	access: function( elems, fn, key, value, chainable, emptyGet, raw ){},
	now: Date.now,
	swap: function( elem, options, callback, args ){},
        buildFragment: function(){}
});           

二、工具方法分析。

9、merge(先講第9個的merge方法,因為下面中很多地方會用到)

①、merge用法,用于數組、類數組、特殊json格式(鍵值為0、1...)的合并。

<script>
var arr1 = ['a','b'];
var arr2 = ['a','b'];
var arr3 = ['a','b'];

var arr4 = ['c','b'];
var json1 = {
	0:'e',
	1:'f',
	length: 2	
}
var json2 = {
	0:'g',
	1:'h'
}
console.log($.merge(arr1,arr4));
console.log($.merge(arr2,json1));   
console.log($.merge(arr3,json2));
</script>           

輸出結果

【jquery源碼五】jQuery工具方法彙總②。

②、merge源碼

merge: function( first, second ) {   //合并數組
    var l = second.length,
	i = first.length,
	j = 0;
    //如果第二個參數的length是數字類型,json格式有legth屬性的也走這裡
    if ( typeof l === "number" ) {   
	for ( ; j < l; j++ ) {
	    first[ i++ ] = second[ j ];
	}
    } else {
	while ( second[j] !== undefined ) {  //json格式的沒有length屬性的走這裡
	    first[ i++ ] = second[ j++ ];
	}
    }

    first.length = i;	//修改合并後的length值

    return first;
}           

1、parseHTML

①、parseHTML用法,将字元串轉化成節點。

<body>
<script src="https://cdn.bootcss.com/jquery/2.0.3/jquery.js"></script>
<script>
var html1 = '<li></li>';
var html2 = '<li></li><li></li><script><\/script>';
console.log($.parseHTML(html1));    //第二個參數指定根節點,預設指定document。
console.log($.parseHTML(html2,document));    //第二個參數指定根節點,有時候有指定到iframe的情況
console.log($.parseHTML(html2,document,true));  //第三個參數是否建立script标簽。預設為false
console.log($.parseHTML(html2,true));   //如果第二個參數為布爾值,源碼中會把布爾值轉到第三個參數
</script>
</body>           

輸出結果

【jquery源碼五】jQuery工具方法彙總②。

像這樣,建立了DOM節點,并包裹在了數組裡面。

②、parseHTML源碼

var rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>|)$/; 	//比對标簽<xxx></xxx>

parseHTML: function( data, context, keepScripts ) { 
	if ( !data || typeof data !== "string" ) { //如果值為空或者值不是string類型,return null
		return null;
	}
	if ( typeof context === "boolean" ) { //當二個參數是布爾值的時候,将第二個參數轉到第三個參數
		keepScripts = context;
		context = false;
	}
	context = context || document;		//指定根節點(有時候要填入的是iframe中的document)

	var parsed = rsingleTag.exec( data ),	//判斷是否是單标簽<div></div>
		scripts = !keepScripts && [];	//如果keepScript沒有值或者為false,則scripts=[]	
										//如果keepScript的值true,即scripts = false
	// Single tag
	if ( parsed ) {
		return [ context.createElement( parsed[1] ) ];  //單标簽直接建立節點
	}

	parsed = jQuery.buildFragment( [ data ], context, scripts );  //建立文檔碎片的形式建立DOM節點
//$.parseHTML(html2,document) -> jQuery.buildFragment( ['<li></li><li></li><script><\/script>'], document, []);
//$.parseHTML(html2,document,true) ->jQuery.buildFragment( ['<li></li><li></li><script><\/script>'], document, false);

	if ( scripts ) {	//scripts = []; 為true
		jQuery( scripts ).remove();		//移除scripts節點
	}

	return jQuery.merge( [], parsed.childNodes );	//通過jQuery.merge進行合并
}
</script>           

$.parseHTML(html2,document)   最終是走-》

jQuery.buildFragment( ['<li></li><li></li><script><\/script>'], document, []);

$.parseHTML(html2,document,true)  最終是走-》

jQuery.buildFragment( ['<li></li><li></li><script><\/script>'], document, false);

接下來看看jQuery.buildFragment的源碼。

17、buildFragment

①、buildFragment用法,将字元串轉化成節點。(這方法一般是jQuery内部使用)

var html2 = '<li>1</li><li>2</li><script>console.log(123)<\/script>';
console.log($.buildFragment([html2],document,[]));
console.log($.buildFragment([html2],document,false));           

輸出結果

【jquery源碼五】jQuery工具方法彙總②。

2、parseXML

①、parseXML用法,将XML格式資料轉化成DOM節點。

var xml = "<rss version='2.0'><channel><title>xml Msg</title></channel></rss>";
var xmlDoc = $.parseXML(xml);

console.log(xmlDoc);           

輸出結果

【jquery源碼五】jQuery工具方法彙總②。

②、parseXML源碼。

parseXML: function( data ) {
	var xml, tmp;
	if ( !data || typeof data !== "string" ) {  //資料必須不為空,并且必須為字元串類型
		return null;
	}
	// Support: IE9
	try {
		tmp = new DOMParser();     //建立解析XML的一個執行個體對象(原生js)IE8及IE8以下不支援
		xml = tmp.parseFromString( data , "text/xml" );   //原生js建立DOM對象
	} catch ( e ) {
		xml = undefined;    //如果出錯資料有錯,IE9會走這裡
	}
	//如果報錯Firefox建立一個<parsererror>這裡是錯誤資訊</parsererror>标簽
	if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) {
		jQuery.error( "Invalid XML: " + data );  //輸出錯誤資訊
	}
	return xml;
}           

3、noop

①、noop就是一個空函數,沒有實際意義。

相當于我們建立一個變量,并且這個變量,在後面的程式中會被指派成string類型。這時我們習慣在程式頭部var str = "" 這樣去初始化這個變量。

4、globalEval

①、globalEval,是用來将局部變量變成全局的。

function fn(){
	//var msg = "局部的";
	$.globalEval('var msg = "局部的";');	
}
fn();
console.log(msg);           

輸出結果

【jquery源碼五】jQuery工具方法彙總②。

②、globalEval源碼。

globalEval: function( code ) {    
	var script,
	    indirect = eval;

	code = jQuery.trim( code );

	if ( code ) {
		if ( code.indexOf("use strict") === 1 ) {   //如果嚴格模式建立script标簽
			script = document.createElement("script");
			script.text = code;
			document.head.appendChild( script ).parentNode.removeChild( script );
		} else {	//非嚴格模式用eval
			indirect( code );     
		}
	}
}           

這裡需要注意的是,在代碼中直接用eval()去解析代碼,沒辦法把代碼解析到全局,但是通過window.eval()或者像源碼中把eval指派給一個變量,也可以。

5、camelCase

①、camelCase,是用來将有"-"的參數轉成駝峰形式。例如font-size轉成fontSize

var str1 = "font-size";
var str2 = "-ms-flex";
var str3 = "-o-flex";
var str4 = "-moz-flex";
var str5 = "-webkit-flex";
console.log($.camelCase(str1));     
console.log($.camelCase(str2));    
console.log($.camelCase(str3));     
console.log($.camelCase(str4)); 
console.log($.camelCase(str5));            

輸出結果

【jquery源碼五】jQuery工具方法彙總②。

這裡需要注意的是“-ms-flex”最終被轉成msFlex,但是“-webkit-flex”最終被轉成了WebkitFlex,這就是-ms-字首與其他浏覽器字首的差別了。

②、camelCase源碼。

var rmsPrefix = /^-ms-/,
	rdashAlpha = /-([\da-z])/gi,
	fcamelCase = function(all,letter){
		return letter.toUpperCase();	
	};
camelCase: function( string ) {  
    return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
    //第一個replace把-ms-替換成ms, 第二個replace把-webkie替換成Webkit,把-size替換成Size
}           

replace中的回調函數fcamelCase = function( all, letter ){},第一個參數是正則擷取到整體部分,第二個參數就是正則擷取到的子項。例如-webkit-flex擷取到的all、letter為

【jquery源碼五】jQuery工具方法彙總②。

6、nodeName

①、nodeName檢測DOM節點的名字

<body>
<div></div>
<script>
    var oDiv = document.getElementsByTagName('div')[0];
    console.log($.nodeName(document.documentElement, 'html'));
    console.log($.nodeName(document.body, 'body'));
    console.log($.nodeName(oDiv, 'div'));
</script>
</body>           

輸出結果

【jquery源碼五】jQuery工具方法彙總②。

②、nodeName源碼。

nodeName: function( elem, name ) {
    return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
}           

7、makeArray

①、makeArray 是把其他資料類型轉成數組類型

<body>
<div>1</div>
<div>2</div>
<script>
    var num = 123;
    var str = 'freddy';
    var aDiv = document.getElementsByTagName('div');
    var json = {
	say: function(){
		console.log(123);
	}	
    };

    console.log( $.makeArray(num) );
    console.log( $.makeArray(str) );

    console.log( aDiv );
    console.log( $.makeArray(aDiv) );  //将類數組轉換成數組

    console.log( $.makeArray(num,json));  //第二個參數傳入json格式,一般是jQuery源碼内部使用
</script>
</body>           

輸出結果

【jquery源碼五】jQuery工具方法彙總②。

②、makeArray 源碼

makeArray: function( arr, results ) {      //轉成數組
    var ret = results || [];

    if ( arr != null ) { 
	if ( isArraylike( Object(arr) ) ) { //判斷是否類數組,而且isArraylike隻能判斷對象類型的參數(string類型也會走這裡)
	    jQuery.merge( ret,
	        typeof arr === "string" ?
		[ arr ] : arr
	    );
	} else {
		[].push.call( ret, arr );    //直接push到數組裡 
	}
    }
    return ret;
}           

8、inArray

①、inArray 其實就是引用了數組方法中的indexO方法

inArray: function( elem, arr, i ) {    //i是查找的索引起始位置
	return arr == null ? -1 : [].indexOf.call( arr, elem, i );
}           

10、grep

①、grep用法 過濾數組中的資料,并得到新數組

其實原生ECMA5中已經有了數組方法[].fiilter(funciton(item, index, array){})方法了。而這個jQuery版本中并沒引用filter方法。

var arr = [1,2,3,4,5];
var arr2 = arr.filter(function(item,index,array){  //原生js數組方法,filter方法
	return item>2;	
});
var arr3 = $.grep(arr, function(item,index){
	return item>2;
});
console.log(arr2);
console.log(arr3);           
【jquery源碼五】jQuery工具方法彙總②。

②、grep 源碼(除了引用filter方法外,我們可以看看,jQuery中是怎樣實作這方法的)

grep: function( elems, callback, inv ) {     //過濾數組中的資料,并得到新數組
	var retVal,
		ret = [],
		i = 0,
		length = elems.length;
		inv = !!inv;       //第三參數如果為true,就是取反的意思
		//比如我要擷取到數組中大于2的值,如果為true擷取到的就是小于2的值。
	for ( ; i < length; i++ ) {
		retVal = !!callback( elems[ i ], i );
		if ( inv !== retVal ) {
			ret.push( elems[ i ] );
		}
	}

	return ret;
}           

11、map

①、map用法 處理數組中的資料,并得到新數組

同樣,原生ECMA5中已經有了數組方法[].map(funciton(item, index, array){})方法了。而這個jQuery版本中并沒引用map方法。

var arr = [1,2,3,4,5];
var arr2 = arr.map(function(item,index,array){  //原生js數組方法 map方法
	return item*2;	
});
var arr3 = $.map(arr, function(item,index){
	return item*2;
});
console.log(arr2);
console.log(arr3);           
【jquery源碼五】jQuery工具方法彙總②。

②、map 源碼(除了引用map方法外,我們可以看看,jQuery中是怎樣實作這方法的)

map: function( elems, callback, arg ) { 	//第三參數是jQuery源碼内部使用
	var value,
		i = 0,
		length = elems.length,
		isArray = isArraylike( elems ),
		ret = [];

	if ( isArray ) {   //數組,類數組走這裡
		for ( ; i < length; i++ ) {
			value = callback( elems[ i ], i, arg );
			if ( value != null ) {
				ret[ ret.length ] = value;
			}
		}
	} else {	//特殊json走這裡
		for ( i in elems ) {
			value = callback( elems[ i ], i, arg );
			if ( value != null ) {
				ret[ ret.length ] = value;
			}
		}
	}
	return core_concat.apply( [], ret );
}           

12、guid

①、guid是給事件函數添加一個唯一的辨別符的,每個事件函數的guid屬性值都是不一樣的。

13、proxy

①、proxy 是用于改變this指向的。

var name = 'window';
var sayName = function(){
	console.log(this.name);
}
var freddy = {
	name: 'freddy',
};

sayName();
$.proxy(sayName,freddy)();
           

②、proxy 源碼。

proxy: function( fn, context ) {
	var tmp, args, proxy;

	if ( typeof context === "string" ) {
		tmp = fn[ context ];
		context = fn;
		fn = tmp;
	}

	if ( !jQuery.isFunction( fn ) ) {
		return undefined;
	}

	args = core_slice.call( arguments, 2 );
	proxy = function() {
		return fn.apply( context || this, args.concat( core_slice.call( arguments ) ) );
	};

	proxy.guid = fn.guid = fn.guid || jQuery.guid++;

	return proxy;
}           

15、now

①、now 是用于擷取目前時間的方法。

②、now源碼

now: Date.now     //直接引用原生js的Date.now方法