(242-528)

我们的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  属性包含表示不同浏览器特性或漏洞的属性集。截止源码到这里还是引用的空对象,后面根据功能支持会产生属性。

 

你可能感兴趣的:((242-528))