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源代码啦,是不是很开心呢!