概述
// 工具方法不仅可以给jQuery对象来用,也可以给原生js来用
// 是jQuery底层最基本的架构
// 调用了之前写的jQuery.extend() 扩展jQuery 静态方法
// jQuery.extend({a: 'XXX', b: 'XXX'});
expando: 生成唯一JQ字符串(内部)
noConflict(): 防止冲突
isReady: DOM是否加载完(内部)
readyWait: 等待多少文件的计数器(内部)
holdReady(): 推迟DOM触发
ready(): 准备DOM触发
isFunction(): 是否为函数
isArray(): 是否为数组
isWindow(): 是否为window
isNumeric(): 是否为数字
type(): 判断数据类型
isPlainObject(): 是否为对象自变量
isEmptyObject(): 是否为空的对象
error(): 抛出异常
parseHTML(): 解析节点
parseJSON(): 解析JSON
parseXML(): 解析XML
noop(): 空函数
globalEval(): 全局解析JS
camelCase(): 转驼峰
nodeName(): 是否为指定节点名(内部)
each(): 遍历集合
trim(): 去前后空格
makeArray(): 类数组转真数组
inArray(): 数组版indexOf
merge(): 合并数组
grep(): 过滤新数组
map(): 映射新数组
guid: 唯一标识符(内部)
proxy(): 改this指向
access(): 多功能值操作(内部)
now(): 当前时间
swap(): CSS交换(内部)
方法详解
expando
: 生成唯一字符串(内部)// expando // "jQuery" + 版本号 + 随机小数 通过replace替换为空 expando: "jQuery" + ( core_version + Math.random() ).replace( /\D/g, "" )
noConflict
: 防止冲突noConflict: function( deep ) { if ( window.$ === jQuery ) { window.$ = _$; } // 如果可以调用这个得写成 // var miaov = $.noConflict(true); // 如果不写为true,会出错 if ( deep && window.jQuery === jQuery ) { window.jQuery = _jQuery; } // 要返回jQuery对象,要不jQuery对象就无法调用了 return jQuery; }
/* 如果在外边定义过$变量,将jquery库引入的时候,因为是立即执行函数,会立即执行里面的代码,当执行到最后的时候,会更改$变量,所以在最开始,先用_jQuery和_$变量将之前的$或jQuery保存下来,如果需要之前的变量,直接调用这个函数就可以返回去,同时这个函数返回的是jQuery对象 */ var $ = 123; <script src="jquery-2.0.3.js"></script> _jQuery = window.jQuery, _$ = window.$, //123
<script>var $ = 123;</script> <script src="jquery-2.0.3.js"></script> <script> var miaov = $.noConflict(); miaov(function(){ alert($); // 这里的输出也是123,这里就用到了if判断 }); </script>
- jQuery中有关DOM加载的代码
isFunction
判断是否为函数// 在低版本浏览器中,typeof 和 下面的 type方法会返回Object 而不是function isFunction: function( obj ) { return jQuery.type(obj) === "function"; }
// 适应各种情况的isFunction function isFunction(fn){ if(!fn) return false; var s = "toString", v = "valueOf", t = typeof fn[s] === "function" && fn[s], o = typeof fn[v] === "function" && fn[v], r; if(t) delete fn[s]; if(o) delete fn[v]; r = typeof fn !== "string" && !(fn instanceof String) && !fn.nodeName && fn.constructor != Array && /^[\s[]?function/.test(fn + ""); if(t) fn[s] = t; if(o) fn[v] = o; return r; }
isArray
是否是数组// 在ECMA5中已经有这个方法了 isArray: Array.isArray
isWindow
是否是窗口isWindow: function( obj ) { // 只有window对象具有window属性 return obj != null && obj === obj.window; }
alert($.isWindow(window)) // 除了null和undefined之外任何跟null比较都会返回false alert(null == null) // true alert(undefined == null) //true // window可以充当全局的对象 var a = 10; window.a = 10; //window可以当浏览器窗口 window.open();
isNumeric
是否是数字值// isNumeric 判断是不是数字类型 // parseFloat(obj) 只要可以转换的,肯定不是NaN // 转换不了的,肯定都是NaN // 这个数字是否在计算机的计算能力范围之内 isFinite( obj ) isNumeric: function( obj ) { return !isNaN( parseFloat(obj) ) && isFinite( obj ); }
type
判断数据类型type: function( obj ) { if ( obj == null ) { // null undefined return String( obj ); } // 如果是在火狐5.1以下用typeof来判断正则,会返回function ;高版本会返回object // Support: Safari <= 5.1 (functionish RegExp) // class2type = {} // core_toString = class2type.toString; return typeof obj === "object" || typeof obj === "function" ? class2type[ core_toString.call(obj) ] || "object" : typeof obj; }
var a = 'hello'; alert($.type(a)); alert( {}.toString.call([]) ) // [object Array] alert( {}.toString.call(new Date) ) // [object Data]
jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) { class2type[ "[object " + name + "]" ] = name.toLowerCase(); });
isPlainObject
判断是否为对象自变量// isPlainObject 判断是否为对象自变量 isPlainObject: function( obj ) { // nodeType是结点的类型,因为如果把一个DOM结点放到type类型中,会返回object;window放到type当中,也会返回object if ( jQuery.type( obj ) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { return false; } try { // class2type = {} // hasOwnProperty 是查看这个属性是否在这个对象身上 // core_hasOwn = class2type.hasOwnProperty // isPrototypeOf 是object对象所特有的,检查这个属性就可以看出是否是object对象 // window.location 如果频繁调用的话,在Firefox < 20 以下的版本会出现内存泄漏的问题,所以需要用try..catch if ( obj.constructor && !core_hasOwn.call( obj.constructor.prototype, "isPrototypeOf" ) ) { return false; } } catch ( e ) { return false; } return true; }
isEmptyObject
是否是空对象isEmptyObject: function( obj ) { var name; for ( name in obj ) { return false; } return true; }
function Aaa(){} var obj = [] // true var obj = {} // true var ob = new Aaa(); // true
error
抛出异常error: function( msg ) { throw new Error( msg ); }
$.error('这是错误');
parseHTML
解析节点// parseHTML 解析结点 // 解析data,将data添加到document中,但没有建立关系,返回节点数组,可以自己建立关系 parseHTML: function( data, context, keepScripts ) { if ( !data || typeof data !== "string" ) { return null; } if ( typeof context === "boolean" ) { keepScripts = context; context = false; } context = context || document; // //如果不是单个标签那么parsed就是null,所谓的单个标签就是<div/>或者<div></div>但是<div>hello</div>不满足! var parsed = rsingleTag.exec( data ), // 判断是否要复制script标签 // 如果keepScripts是false,将script标签全部复制到script数组中,最后从DOM中移除 // 如果keepScripts是true,script是false,不需要将script复制到数组中 scripts = !keepScripts && []; // Single tag 单标签的创建 if ( parsed ) { //如果是单个标签就调用相应的createElement方法,默认上下文是document! return [ context.createElement( parsed[1] ) ]; } // 多标签的创建 // 如果需要创建script标签 scripts为[] 不需要false parsed = jQuery.buildFragment( [ data ], context, scripts ); // 到这里scripts里面会有script标签 if ( scripts ) { // 将DOM中的script数组里的东西移除 jQuery( scripts ).remove(); } // 最后得到的是DOM结点,把它转成数组 return jQuery.merge( [], parsed.childNodes ); }, var str = '<li></li><li></li>'; console.log($.parseHTML(str)); // [li, li] // document 指定的根节点 // boolean 是否会储存script标签 str = '<li></li><li></li><script></script>'; console.log($.parseHTML(str, document, true)); // [li, li, script]
parseXML
解析XMLparseXML: function( data ) { var xml, tmp; if ( !data || typeof data !== "string" ) { return null; } // Support: IE9 // 用DOMParse去解析XML文档的时候,前提是data必须是完整的XML,也不能去解析HTML,所以需要try..catch(在IE9下才会报错) try { // 在IE6和IE7中没有这个方法,应该怎么办呢? // ActiveXObject也可以做低版本的XML解析 tmp = new DOMParser(); xml = tmp.parseFromString( data , "text/xml" ); } catch ( e ) { xml = undefined; } // 如果其他浏览器,上面parseFromString不会报错,但是会创建一个parserror的标签 if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) { jQuery.error( "Invalid XML: " + data ); } return xml; }
parseXML: function(data, xml, tmp) { if (window.DOMParser) { //标准 IE9可以 tmp = new DOMParser(); xml = tmp.parseFromString(data, "text/xml"); } else { // IE xml = new ActiveXObject("Microsoft.XMLDOM"); xml.async = "false"; xml.loadXML(data); } tmp = xml.documentElement; if (!tmp || !tmp.nodeName || tmp.nodeName === "parsererror") { jQuery.error("Invalid XML: " + data); } return xml; }
noop
返回一个空函数// noop 返回一个空函数 // 在做插件或做组件开发的时候,经常会写一些默认参数,默认参数有可能就是一个函数 noop: function() {}
globalEval
全局解析JSglobalEval: function( code ) { var script, // 先把eval存成indirect indirect = eval; // 用trim可以把前后空格去一下 code = jQuery.trim( code ); if ( code ) { // 在严格模式下,是不支持eval解析的 if ( code.indexOf("use strict") === 1 ) { script = document.createElement("script"); script.text = code; // 大概就是先通过创建script标签,将变量添加到全局,之后再删除这个标签 document.head.appendChild( script ).parentNode.removeChild( script ); } else { // 正常情况下,就通过eval解析就可以了 indirect( code ); } } }
function test() { jQuery.globalEval("var newVar = true"); } test(); function test() { eval('var a = 1'); // 通过eval可以解析里面的内容 alert(a); // 在这里可以进行输出 } test(); alert(a); // 在这里看不到里面的内容 如果是window.eval('var a = 1'); 就可以正常输出了 function test() { // 这样写也是可以的 var val = eval; val('var a = 1'); alert(a); } test();
camelCase
转驼峰的方法// camelCase 是转驼峰的方法 // 把margin-top -> marginTop camelCase: function( string ) { // rmsPrefix = /^-ms-/ // 将-ms-transform -> ms-Transform // replace 是将(-字符)这个组合转化成大写字符 // rdashAlpha = /-([\da-z])/gi /* fcamelCase = function( all, letter ) { return letter.toUpperCase(); } */ return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); }
fcamelCase = function( all, letter ) { return letter.toUpperCase(); } // 在IE浏览器下的方法 -ms-transform -> msTransform // 在其它浏览器下得大写 -moz-transform -> MozTransform
nodeName
是否为指定节点名(内部)nodeName: function( elem, name ) { return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); }
$.nodeName(document.documentElement, 'html'); // true $.nodeName(document.body, 'html'); // false $.nodeName(document.body, 'body'); // body
each
遍历数组each: function( obj, callback, args ) { var value, i = 0, length = obj.length, isArray = isArraylike( obj ); // 判断是不是内部使用的 if ( args ) { // 如果是数组 if ( isArray ) { for ( ; i < length; i++ ) { // 参数数量不固定,使用apply value = callback.apply( obj[ i ], args ); if ( value === false ) { break; } } } else { // 如果是JSON for ( i in obj ) { value = callback.apply( obj[ i ], args ); if ( value === false ) { break; } } } // 如果是外部使用的 } else { if ( isArray ) { for ( ; i < length; i++ ) { // 参数数量固定,使用call // 第一个参数是谁调用callback,后面的是传入callback中的参数 value = callback.call( obj[ i ], i, obj[ i ] ); if ( value === false ) { break; } } } else { for ( i in obj ) { value = callback.call( obj[ i ], i, obj[ i ] ); if ( value === false ) { break; } } } } return obj; }
isArraylike
是否是数组或是类数组/** * 三种情况下返回true: * 1. length == 0 * 2. jQuery.type(obj) == 'Array'; * 3. length === "number" && length > 0 && ( length - 1 ) in obj * * document.getElementById("xx")返回的nodeType是1,但是没有length属性 * document.getElementsByTagName("div")的集合有length属性,但是没有nodeType,返回的是htmlCollection集合 * * 什么时候nodeType是1,同时也具有length属性呢? * isArraylike($("#content")) //打印true */ function isArraylike( obj ) { // length得为0或number类型 var length = obj.length, type = jQuery.type( obj ); if ( jQuery.isWindow( obj ) ) { return false; } // if ( obj.nodeType === 1 && length ) { return true; } return (type === "array") || (type !== "function" && // 特殊的JSON或者arguments 类数组 ( length === 0 || // 如果当length = 0的时候,-1 in obj 是错误的语法格式,所以得先判断lenght === 0 typeof length === "number" && length > 0 && ( length - 1 ) in obj )); }
这里之所以要判断(length-1) in obj是引入了稀疏数组的概念,稀疏数组在用forEach或者map迭代的时候会跳过空隙(这两个方法第一个参数是元素,第二个参数是下标,第三个是调用数组),也就是要确保数组的最后一个元素是存在的!如果要创建密集数组可以通过Array.apply(arr,Array(3))的方式,于是forEach和map就不会跳过其中的元素了
trim
去掉前后空格// trim 去掉前后空格 trim: function( text ) { // core_version = "2.0.3" // core_trim = core_version.trim return text == null ? "" : core_trim.call( text ); }
makeArray
类数组转化成数组// makeArray() 类数组转化为真数组 // core_deletedIds = [] // core_push = core_deletedIds.push makeArray: function( arr, results ) { // 首先看有没有特殊的JSON var ret = results || []; // 判断要转化为数组的东西是否为空 if ( arr != null ) { // isArraylike 只能针对的是对象,如果是123之类,得先转换成Object,数字调用的就是else了 // 如果是字符串,object(xxx),转为JSON,走if // 字符串,数组,类数组都走这里 if ( isArraylike( Object(arr) ) ) { // 内部转换,使用下面这个 // 通过merge进行合并 jQuery.merge( ret, typeof arr === "string" ? [ arr ] : arr ); } else { // 普通使用,直接使用数组的push方法 // 将数字(111)push到数组中 core_push.call( ret, arr ); } } return ret; }
// $.makeArray("xxx",[1,2]) [1,2,xxx] /** * var obj={ 0:"xx", 1:"female", length:2 } var arr=[1,2,3]; $.merge(arr,obj); [1, 2, 3, xx, female] */ /** * var obj = {0:"xxx", 1:"yy", length:2}; var result = $.makeArray("sex", obj); [xxx, yy, sex] */
inArray
数组版的indexOfinArray: function( elem, arr, i ) { return arr == null ? -1 : core_indexOf.call( arr, elem, i ); }
var arr = ['a', 'b', 'c', 'd']; $.inArray('b', arr); // 1 $.inArray('e', arr); // -1
merge
合并数组// merge 合并数组 // 对外就是只针对数组 // 对内就是转换成特殊形式的JSON merge: function( first, second ) { var l = second.length, i = first.length, j = 0; // $.merge(['a', 'b'], ['c', 'd']); if ( typeof l === "number" ) { for ( ; j < l; j++ ) { first[ i++ ] = second[ j ]; } // $.merge(['a', 'b'], {0: 'c', 1: 'd'}); // $.merge({0: 'c', 1: 'd', length: 2}, {0: 'c', 1: 'd'}); } else { while ( second[j] !== undefined ) { first[ i++ ] = second[ j++ ]; } } first.length = i; return first; }
grep
过滤新数组// grep 过滤新数组 grep: function( elems, callback, inv ) { var retVal, ret = [], i = 0, length = elems.length; // 如果不写这个参数(inv),得到的结果是undefined,可以转化成false inv = !!inv; for ( ; i < length; i++ ) { retVal = !!callback( elems[ i ], i ); if ( inv !== retVal ) { ret.push( elems[ i ] ); } } return ret; }
var arr = [1, 2, 3, 4]; arr = $.grep(arr, function(n, i){ return n > 2; }) console.log(arr); // [3, 4] // 过滤到相反的方式 var arr = [1, 2, 3, 4]; arr = $.grep(arr, function(n, i){ return n > 2; }, true) console.log(arr); // [1, 2]
map
映射新数组// map 映射新数组 // 最后一个参数 arg 是内部使用的 map: function( elems, callback, arg ) { 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; } } // JSON } else { for ( i in elems ) { value = callback( elems[ i ], i, arg ); if ( value != null ) { ret[ ret.length ] = value; } } } // 不想得到复合数组,只想得到单一的数组 // core_deletedIds = [] // core_concat = core_deletedIds.concat /* var arr = [1, 2, 3, 4]; arr = $.map(arr, function(n){ return [n + 1]; // [2, 3, 4, 5] }); 如果不使用下面这句话 [[2], [3], [4], [5]] */ return core_concat.apply( [], ret ); }
var arr = [1, 2, 3, 4]; arr = $.map(arr, function(n){ return n + 1; });
guid: 1
唯一标识符(内部使用)// guid: 1 唯一标识符(内部使用) // 通过标识将函数和标识建立一个关系,所以可以取消绑定 function show(){ alert(this); } //下面这两种情况都可以取消绑定 //$('input:eq(0)').click(show, window); 这样的话,show就不是一个时间函数了,而是一个普通函数,绑定到了window身上,同时弹出 [object window] $('input:eq(0)').click(show); // [object HTMLInputElement] $('input:eq(1)').click(function(){ $('input:eq(0)').off(); }); // <input type="button" value="点击"> // <input type="button" value="取消绑定">
proxy
改变this指向// proxy 改变this指向 function show(){ alert(this); } // 第一个是你想要调用的函数,第二个参数是你想让this指向谁 $.proxy(show, document)(); function show(n1, n2){ alert(this); } // 在外面传参可以在触发函数的时候传参 $.proxy(show, document)(3, 4); // 在里面传参可以在不触发函数的时候传参 $.proxy(show, document, 3, 4)(); $.proxy(show, document, 3)(4); proxy: function( fn, context ) { var tmp, args, proxy; // 如果指向是一个字符串 /* var obj = { show: function(){ alert(this); } }; $(document).click($.proxy(obj.show, obj)); //简写方式 $(document).click($.proxy(obj, 'show')); */ if ( typeof context === "string" ) { // tmp = obj[show] tmp = fn[ context ]; // context = obj context = fn; // fn = obj[show] fn = tmp; } if ( !jQuery.isFunction( fn ) ) { return undefined; } // $.proxy(show, document, 3)(4); // arguments: show, document, 3 // 截取2之后的 3 args = core_slice.call( arguments, 2 ); proxy = function() { // arguments是一种类数组,不是真正的数组,所以得通过 core_slice.call( arguments ) 转化成真正的数组 4 return fn.apply( context || this, args.concat( core_slice.call( arguments ) ) ); }; // 设置唯一的标识,在后期有用 proxy.guid = fn.guid = fn.guid || jQuery.guid++; return proxy; }
now: Date.now
Date.now 的性能比 (new Date()).getTime() 要好
swap
css交换// swap css交换(内部) // jQuery方法,可以获取到隐藏元素的宽 // 如何获取的呢? // 首先储存 display: none // 再把 display: none 换成 display: block; visibility: hidden; position: absolute // 获取到值 // 再把 display: block; visibility: hidden; position: absolute 换成 display: none // <div id="div1" style="display: block; visibility: hidden; position: absolute">aaaa</div> $('#div1').width() // 原生方法,获取不到隐藏元素的宽 $('#div1').get(0).offsetWidth <div id="div1" style="width:100px; height:100px; background:red; display: none">aaaa</div> swap: function( elem, options, callback, args ) { var ret, name, old = {}; // Remember the old values, and insert the new ones for ( name in options ) { old[ name ] = elem.style[ name ]; elem.style[ name ] = options[ name ]; } ret = callback.apply( elem, args || [] ); // Revert the old values for ( name in options ) { elem.style[ name ] = old[ name ]; } return ret; }
access
多功能值操作$(function(){ // 获取值 var a = $('#div1').css('width'); // 设置值 $('#div1').css('background', 'yellow'); $('#div1').css({ background: 'yellow', width: '300px' }); }); <div id="div1" style="width:100px; height:100px; background:red">aaaa</div> // elems 是操作的元素 // fn 回调函数 有区别的地方,就在回调函数当中做进一步处理 // key 就是background之类 // value 就是red之类 // chainable 为false是获取 为true是设置 access: function( elems, fn, key, value, chainable, emptyGet, raw ) { var i = 0, length = elems.length, // 如果key的值为空,bulk为true bulk = key == null; // 设置多组值 // $('#div1').css({ background: 'yellow', width: '300px' }); if ( jQuery.type( key ) === "object" ) { chainable = true; for ( i in key ) { // 递归设置 jQuery.access( elems, fn, i, key[i], true, emptyGet, raw ); } // 设置一组值的情况下 // $('#div1').css('background', 'yellow'); } else if ( value !== undefined ) { chainable = true; if ( !jQuery.isFunction( value ) ) { raw = true; } // 如果key的值为空 if ( bulk ) { // 如果value的值是字符串 if ( raw ) { fn.call( elems, value ); fn = null; // 如果value的值是函数 } else { bulk = fn; fn = function( elem, key, value ) { return bulk.call( jQuery( elem ), value ); }; } } if ( fn ) { for ( ; i < length; i++ ) { fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) ); } } } return chainable ? elems : // Gets bulk ? fn.call( elems ) : length ? fn( elems[0], key ) : emptyGet; }