jQuery源代码学习_工具函数_type

jquery源代码学习_工具函数_type

jquery里面有一个很重要的工具函数,$.type函数用来判断类型,今天写这篇文章,是来回顾type函数的设计思想,深入理解。

首先来看一下最终结果:

上面的源代码乍一看似乎艰涩难懂,有点抽象,毕竟是前辈心血之作,深深佩服。对我们初学者,可以从简单入手,来一步一步深化,得到最终的优化方案。

第一个版本

在看《JavaScript高级程序设计》这本书时候,书上提到typeof,typeof可以判断function,number ,undefined, string, boolean但是用typeof 判断null会返回object,而用typeof判断object返回object,且对于任意的系统对象,比如Regexp,Date,Array等,并不能很好区分。








也就是说:不是object类型的可以使用typeof判断;而对于其他引用类型,我们使用Object.protype.toString。所以可以得到第一个版本的代码如下:

function type(obj) {
    if (typeof obj !== 'object') {
        return typeof obj;
    }
    return Object.prototype.toString.call(obj).slice(8, -1).toLowerCase();
}

对于一些系统对象,用到slice(8,-1),省略了前缀"[object "。不然我们会得到这样结果:

问题
这个函数不能处理type(document.createElement('div'))这样的情况。示例如下:

显然:实际上应该是object类型,而htmldivelement并不是JavaScript数据类型,而是一个DOM的数据类型。


第二个版本

吸取第一个版本的教训,也就是说类型的判断不是盲目的,判断的结果应该在我们所掌握的JavaScript几种数据类型里,那么是哪几种类型呢?除了基本的值类型5种,引用类型Object,还包括Function Array Date RegExp Error一共有11种,因此进一步优化的结果应该是:我们判断的类型取自于以上11种类型里面的任意一个。经常分析处理,我们得到第二个版本的代码如下:

var class2type = 'Boolean Number String Null Undefined Function Array Date RegExp Object Error';

function type(obj) {
    if (typeof obj !== 'object') {
        return typeof obj;
    }
    var sliced = Object.prototype.toString.call(obj).slice(8, -1);
    return class2type.indexOf(sliced) >= 0 ? sliced.toLowerCase() : 'object';
}

显然我们第一个版本的问题已经解决了。indexOf()方法可返回某个指定的字符串值在字符串中首次出现的位置。

问题
计算量大,每判断一次都要用一个slice、一个indexOf、一个toLowerCase


第三个版本

很明显我们规避使用indexOf方法,把所有的类型结果用对象字面量的方式表现出来,其中key是Object.prototype.toString的计算结果,value是type结果,因此我们可以得到第三个版本的代码如下:

var class2type = {
    '[object Boolean]': 'boolean',
    '[object Number]': 'number',
    '[object String]': 'string',
    '[object Function]': 'function',
    '[object Undefined]': 'undefined',
    '[object Null]': 'null',
    '[object Array]': 'array',
    '[object Date]': 'date',
    '[object RegExp]': 'regexp',
    '[object Object]': 'object',
    '[object Error]': 'error'
};

function type(obj) {

    if (typeof obj !== 'object') {
        return typeof obj;
    }

    return class2type[Object.prototype.toString.call(obj)] || 'object';
}

这里用class2type来缓存类型判断的结果。

缺点
这个对象里面全部是字符串,没有办法进行压缩。


第四个版本

借鉴第3种方法的缺陷,我们不用字面量的形式,采用split方法,用空格分隔开类型。

var class2type = {};
var arr = 'Boolean Number String Function Array Null Undefined Date RegExp Object Error'.split(' ');

for (var i = 0, l = arr.length; i < l; i++) {
    class2type['[object ' + arr[i] + ']'] = arr[i].toLowerCase();
}

function type(obj) {

    if (typeof obj !== 'object') {
        return typeof obj;
    }

    return class2type[Object.prototype.toString.call(obj)] || 'object';

}

这样就不需要枚举所有的类型了,如果需要做额外的判断,只要在字符串中加一个单词就可以了。


第五个版本

继续优化,减少代码量,更高效的方法是用到正则表达式,来匹配每一种类型,它们之间用空格隔开。(据说是司徒正美大神倡导的方法,果然厉害,哇)


var class2type = {};

// 利用replace减少使用for循环,同时可以简化代码
'Boolean Number String Function  Undefined Null Array Date RegExp Object Error'.replace(/\w+/g, function (name) {
    class2type['[object ' + name + ']'] = name.toLowerCase();
});

function type(obj) {

    if (typeof obj !== 'object') {
        return typeof obj;
    }

    return class2type[class2type.toString.call(obj)] || 'object';

}

这样,层层递进,就得到我们伟大的jQuery type源代码啦,是不是很开心呢!


posted @ 2015-03-26 15:28  青青flye  阅读(248)  评论(0编辑  收藏  举报