前言:上篇文章已经分析了16个常用工具方法了,现在来继续分析后面的17个常用工具方法。
【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>
输出结果
②、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>
输出结果
像这样,创建了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));
输出结果
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);
输出结果
②、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);
输出结果
②、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));
输出结果
这里需要注意的是“-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为
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>
输出结果
②、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>
输出结果
②、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);
②、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);
②、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方法