我们的extend在前面已经分析过了,我们执行jQuery.extend({}),实际就是向jQuery函数同名对象中添加属性。
1. expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), ,随机生成一组字符串,这是当前页面jQuery的唯一标识,几乎不可能被外部访问到。
isReady: true, jQuery是否准备好,先假设为true, error 方法后面讲。 jQuery.noop 就是一个空函数,比如当一个函数需要传入回调函数,我们又不需要执行回调的时候,传入它就可以了。
2.在学习 isFunction 之前我们先研究一下其辅助 type 工具
var class2type = {} var toString = class2type.toString; jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) { class2type[ "[object " + name + "]" ] = name.toLowerCase(); }); function type( obj ) { if ( obj == null ) { return obj + '' ; } // Support: Android < 4.0, iOS < 6 (functionish RegExp) return typeof obj === "object" || typeof obj === "function" ? class2type[ toString.call(obj) ] || "object" : typeof obj; }
在JS中,toString在一些类中定义了特定的版本,如数组类的toString是将数组的每个元素转化为字符串,并通过,号结合成结果字符串。其它的一些类也都有相应的特定toString。对象的toString与他们不同(继承自Object.prototype),它将可以返回对象的类。格式为"object class"。注意是下面是对象的toString方法。
console.log(Object.prototype.toString.call([])) //"[object Array]" console.log(Object.prototype.toString.call("")) //"[object String]"
可以利用这个特定判断目标的类型,同时考虑null和undefined
function classOf(o){ if( o == null ) return o + ''; return Object.prototype.toString.call(o).slice(8,-1).toLowerCase(); } console.log(classOf(null)) //"null" console.log(classOf()) //"undefined" console.log(classOf(1)) //"number" console.log(classOf('')) //"string" console.log(classOf(false)) //"boolean" console.log(classOf({})) //"object" console.log(classOf([])) //"array" console.log(classOf(/./)) //"regexp" console.log(classOf(new Date())) //"date" console.log(classOf(window)) //"window"(客户端宿主对象) function f(){} console.log(classOf(new f())) //"object"
jQuery的实现方法也大同小异。考虑了一些对手机兼容的处理。
isFunction 实际就是调用type return jQuery.type(obj) === "function"
3. isArray: Array.isArray, 就是直接引用数组的方法。
isWindow 实现的方法很多,jQuery是通过判断obj对象等同于其window属性。 return obj != null && obj === obj.window;
isNumeric 的主要是通过parseFloat和Number实现的判断。
console.log(parseFloat('123a')); // 123 console.log(Number('123a')); // NaN
parseFloat可以很好的转换为数字,但有一个缺点就是字符串里有字符和数字时,它仍然会转化出数字。而Number可以解决这个问题。
return obj - parseFloat( obj ) >= 0;
这里利用 - 运算符,隐式转化obj为数字,然后通过关系运算符>=转化为布尔型,为什么用>=而不是==,<=,这里parseFloat('0xff') ,当其是以0开头的字符串,值为0,而实际它是数字。 所以 255 - 0>=0;在此基础上作者作了一些兼容,我们自己写的时候也可以按自己需求判断。
4.纯粹对象指的是一般我们{}或new Object()创建的对象,这类对象继承的原型Object.prototype有自身的方法isPrototypeOf。
{}.hasOwnProperty.call( a.constructor.prototype, "isPrototypeOf" )
作者亦加入了一些辅助判断,排除不是对象类型,DOM节点,window对象。
isEmptyObject 作者采用for in 如果有属性则不为空。这里的判断包括继承来的属性
5. globalEval 主要利用eval来实现,下面分析下eval的几个不同表现,ES5下。
//非严格,间接调用,在全局中定义 function a(){ var geval = eval; geval('var h = 1') } a(); console.log(h) //1
//非严格,直接调用,在全局中定义 function a(){ eval('var h = 1') } a(); console.log(h) // h未定义
//严格,直接或非直接调用,在全局中定义 function a(){ var geval = eval; eval('"use strict";var h = 1') eval('"use strict";var h = 1') } a(); console.log(h) // h未定义
在严格模式中,定义的变量是在私有作用域中。
原理明白了,jQuery中的globalEval就很容易理解。它通过判断是否是严格模式,如果是,则创建一个脚本插入到页面中让它执行,如果不是,则利用Eval的,非严格间接调用。
nodeName 判断节点标签名(elem.nodeName)与我们期望的是不是同一个,来判断是不是某类型节点。这方法在jQuery内部使用,比如当判断点击的是不是Input,nodeName(obj,'input')即可。
each,trim之前已经讲过。
6.虽然jQuery是类数组对象,可以[num],访问,也可以像数组那样遍历,但它还缺少一些对数组的内置方法(如.pop()
和.reverse())
。通过makeArray转化之后就可以使用这些方法。
它考虑的情况有类数组对象,和非类数组对象,在分数组对象,就当一般元素直接插入。如果是类数组对象,比如$('div'),则将其元素依次插入空数组并返回。这里还有一个问题需要注意,字符串也是类数组对象,但我们希望它当做字符串处理。考虑到这些东西,源码就很容易理解了。jQuery.merge之前讲过,这里重复一遍它的作用,是将第二个数组类数组的元素,以此插入到第一个数组或类数组中,同时设置了length。
inArray 方法是通过对目标调用indexOf,判断目标是否存在,作用扩大到类数组对象。 indexOf.call( arr, elem, i )
grep 是过滤数组中的元素,功能思想是,遍历,执行回调函数,判断是否是期望的元素,是则插入到新建的期望数组中。最后返回期望数组。
7. guid 针对目标的全局唯一标识。方便移除,事件部分细说。
proxy :如果你了解 Function.prototype.bind ,那么这个方法你就很容易理解。下面给出一个简化的bind
Function.prototype.bind = function (scope) { var fn = this;//这里fn为this,也就是调用bind的函数,方便下面调用 return function () {//返回的是一个可以运行函数 return fn.apply(scope);//利用apply方法,使用scope对象调用fn, }; } var b = { a : 1, } function fn(){ console.log(this); }
fn() //window var fnB = fn.bind(b); fnB()//{a:1}
实现返回一个对原函数指定上下文的函数。
回到源码,作者增加了对如下情况的处理
var b = { a : 1, fn:function(){ console.log(this) } }
它是通过判断第二个是否为字符串,重新指定函数和要绑定的对象。
!jQuery.isFunction( fn ) 判断如果还不是函数,就直接返回undefined;
为实现部分参数调用,源码中的代码如下
args = slice.call( arguments, 2 ); proxy = function() { return fn.apply( context || this, args.concat( slice.call( arguments ) ) ); };
它将先前已经传入的参数和后传入参数合并,做回retrun新函数的参数。
proxy.guid = fn.guid = fn.guid || jQuery.guid++; 因为他们实际是一个函数,这里将原函数和生成函数指定为同一个GUID。方便移除。在事件中重点将Guid;
now: Date.now 当前时间
support 属性包含表示不同浏览器特性或漏洞的属性集。截止源码到这里还是引用的空对象,后面根据功能支持会产生属性。