JQuery源码分析 - 数据类型检测方法封装(数字、对象、数组类数组)

JQuery中的数据类型检测

在本系列的上一篇文章(JQuery源码分析 - 闭包机制在jQuery中的使用及冲突解决)中我们已经分析了闭包机制在jQuery中的使用、不同环境的判断以及冲突的解决方案; 接下来我们继续来分析一下jQuery源码的核心内容中的数据类型检测封装。
对应jQuery源码: 64~72行

//创建一个空对象
var class2type = {
    
    };
//用来检测数据类型
var toString = class2type.toString;//Object.prototype.toString
//用来检测是否是私有属性
var hasOwn = class2type.hasOwnProperty;//Object.prototype.hasOwnProperty
//Object.prototype.hasOwnProperty是一个函数,那么hasOwn肯定也是一个函数,每个函数都是Function的实例
//所以hasOwn.toString 就是Function.prototype.toString
//用来把函数转换为字符串
var fnToString = hasOwn.toString;//Function.prototype.toString
//相当于Function.prototype.toString.call(Object) => 将toString中的this改为Object
//相当于Object.toString(); 注意这里不是Object.prototype.toString()
//把Object变成字符串
var ObjectFunctionString = fnToString.call(Object);"function Object(){...}"
//获取当前对象的原型链__proto__
var getProto = Object.getPrototypeOf;

//在jQuery源码中有这样一段代码:现将字符串切割为数组,然后遍历这个数组给class2type添加以object+数组中的项为名的属性,属性值则是数组中的每一项的小写形式
/*
jQuery.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "), function(_i, name){
	class2type["[object "+name+"]"] = name.toLowerCase();
});
*/
//上面代码我们可以转换为:
//建立数据类型检测映射表
var mapType = ["Boolean", "Number", "String", "Function", "Array", "Date", "RegExp", "Object", "Error", "Symbol"];
mapType.forEach(function(name){
    
    
	class2type["[object "+name+"]"] = name.toLowerCase();
});
//最后class2type将变为如下所示:
/*
class2type={
"[object Boolean]":"boolean",
"[object Number]":"number",
"[object String]":"string"
}
*/

//封装万能的数据类型检测方法
function toType(obj){
    
    
	//如果obj是null或undefined(undefined == null)
	//则返回字符串null或字符串undefined
	if(obj == null) {
    
    
		return obj + "";//obj和字符串相加结果转换为字符串
	};
	//基于字面量方式创建的基本数据类型,直接基于typeof检测即可(性能稍高一些)
	//剩余的则基于Object.prototype.toString.call的方式来检测,把获取的值拿到映射表中进行匹配,
	//得到的值是字符串对应的数据类型
	return typeof obj === "object" || typeof obj === "function" ?
		class2type[toString.call(obj)] || "object" : typeof obj;
}

//检测是否为函数
var isFunction = function isFunction(obj){
    
    
	//元素节点[DOM对象]具备nodeType(元素、文本、注释、document 对应1、3、8、9)
	//typeof obj.nodeType !== "number":防止在部分浏览器中,检测<object>元素对象结果也是"function"
	return typeof obj === "function" && typeof obj.nodeType !== "number";
}

//检测是否是window对象
var isWindow = function isWindow(obj){
    
    
	//window对象的特点:window.window === window
	return obj != null && obj === obj.window;
}

//检测是否为数组或类数组
var isArrayLike = function isArrayLike(obj){
    
    
	//!!obj将obj转换为布尔类型
	//如果!!obj为true并且"length" in obj 为true则获取obj的length值
	//所以length存储的是对象的length属性或false
	//type存储的是检测的数据类型
	var length = !!obj && "length" in obj && obj.length,
		type = toType(obj);
	//window.length = 0 windows有length属性
	//Function.prototype.length = 0 Function的原型也有length属性
	//所以这里是排除window和Function,有length属性不一定就是数组
	if(isFunction(obj) || isWinow(obj)){
    
    
		return false;
	}
	//type === "array"说明是数组
	//length === 空的类数组
	//最后一个条件用于判断是非空数组:有length属性并且length是一个数字类型,length的值大于0,并且最大索引在数组中
	//length - 1就是最大索引
	return type === "array" || length === 0 || 
		typeof length === "number" && length > 0 && (length - 1) in obj;
		//逻辑与的优先级大于逻辑或
}

//检测是否是纯对象,例如:{}
var isPlainObject = function isPlainObject(object){
    
    
	var proto, Ctor;
	//不存在,或基于toString检测的结果都不是[object Object]则一定不是对象
	if(!obj || toString.call(obj) !== "[object Object]"){
    
    
		return false;
	}
	//获取当前值的原型链(直属类的原型链)
	proto = getProto(obj);
	//通过Object.create(null)创建的对象是没有__proto__ 的,所以肯定是纯对象
	if(!proto){
    
    
		return true;
	}
	//Ctor 存储原型对象上的constructor属性,如果没有这个属性就是false
	Ctor = hasOwn.call(proto, "constructor") && proto.constructor;
	//如果构造函数是一个函数,
	//条件成立说明原型上的构造函数是Object: obj就是Object的一个实例,并且obj.__proto__ === Object.prototype
	return typeof Ctor === "function" && fnToString.call(Ctor) === ObjectFunctionString;
	//fnToString.call = Object.prototype.hasOwnProperty.toString.call()
	//ObjectFunctionString = fnToString.call(Object)这句代码在上面已经定义,是将Object转换为字符串
	//而fnToString.call(Ctor)是将Ctor转换为字符串,如果二者相等则条件成立
}

//检测是否为空对象
var isEmptyObject = function isEmptyObject(obj){
    
    
	//jQuery的原生写法,但是优缺点:基于for in循环有很多问题,无法获取Symbol类型的属性,会把自己定义在原型上的属性也获取到等等
	/*
	var name;
	for(name in obj){
		return false;
	}
	return true;
	*/
	//排除null或undefined
	if(obj == null) return false;
	if(typeof obj !== "object") return false;
	//是一个对象,纯对象或特殊对象都可以
	var keys = Object.keys(obj);
	//如果兼容再去拼接
	if(hasOwn.call(Object,"getOwnPropertySymbols")){
    
    
		keys.concat(Object.getOwnPropertySymbols(obj));
	}
	return keys.length === 0;
}

//检测是否为数字
var isNumeric = function isNumeric(obj){
    
    
	var type = toType(obj);
	//纯数字或是数字的字符串形式("10") 并且不是NaN
	//obj - parseFloat(obj)只要有任何一个值不是数字,结果都是NaN, 可以直接用+obj代替
	return (type === "number" || type === "string") && !isNaN(obj - parseFloat(obj));
}

猜你喜欢

转载自blog.csdn.net/lixiaosenlin/article/details/109996711
今日推荐