本文分析的 jquery 版本为 v3.6.0
当我们了解了 Object.prototype.toString 方法用来检测数据类型时,是不是觉得最优的方案就是它了呢,其实未必,因为 typeof 也有它的优势,特别是检测对象类型性能特别好,我们来看下 jquery 中如何处理的~
代码片段分析
var getProto = Object.getPrototypeOf;
// ...
var class2type = {};
var toString = class2type.toString; // Object.prototype.toString
var hasOwn = class2type.hasOwnProperty; // Object.prototype.hasOwnProperty
var fnToString = hasOwn.toString; // Function.prototype.toString
var ObjectFunctionString = fnToString.call( Object ); // 把 Object 构造函数转字符串
// ...
// 匿名函数具名化 是一种好的习惯
var isFunction = function isFunction( obj ) {
return typeof obj === "function" &&
// 为了防止 obj 为 document.createElement("object") 创建一个 Object 标签
// 嵌套 flash 动画的
typeof obj.nodeType !== "number" &&
// 这个也不知道为了检测什么场景 反正我们想不到的 jq 都在做
typeof obj.item !== "function";
};
// 检测是否是一个 window 对象
var isWindow = function isWindow( obj ) {
// window 特性之一:套娃操作 window.window == window
return obj != null && obj === obj.window;
};
var arr = "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " );
// 建立映射表
jQuery.each(arr, function( _i, name ) {
class2type[ "[object " + name + "]" ] = name.toLowerCase();
} );
// {
// '[object Array]': 'array',
// '[object Boolean]': 'boolean',
// ...
// }
// 标准的检测数据类型的方法
var toType = function toType( obj ) {
// 匹配 null 和 undefined
if ( obj == null ) {
return obj + "";
}
// 排除掉 null 和 undefined 后,这里的 object 和 function 都是用 typeof 来检测
// 要学习这种组合用法
return typeof obj === "object" || typeof obj === "function" ?
// 通过映射表 返回一个小写的数据类型(同 typeof 返回)
class2type[ toString.call( obj ) ] || "object" :
typeof obj;
}
// 检测是否为数组或者类数组
function isArrayLike( obj ) {
// 数组和类数组的特性
// @1 都有 length
// @2 都有索引
// @3 都是对象
var length = !!obj && "length" in obj && obj.length,
type = toType( obj );
// 函数和 window 有 length,代表形参个数和 iframe 个数
//
if ( isFunction( obj ) || isWindow( obj ) ) {
return false;
}
return type === "array" || // 是纯数组
// 不是数组 且有 length 是空类数组
length === 0 ||
// 不是数组 且 length > 0 且 最大索引存在在 obj 上
typeof length === "number" && length > 0 && ( length - 1 ) in obj;
}
// 检测是不是一个纯粹对象「直属类是 Object 或者 Object.create(null)」
var isPlainObject = function( obj ) {
var proto, Ctor;
// Detect obvious negatives
// Use toString instead of jQuery.type to catch host objects
if ( !obj || toString.call( obj ) !== "[object Object]" ) {
return false;
}
proto = getProto( obj );
// Objects with no prototype (e.g., `Object.create( null )`) are plain
if ( !proto ) {
return true;
}
// Objects with prototype are plain iff they were constructed by a global Object function
Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor;
// 都转把实例的构造函数和 Object 都转 string 对比
return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString;
}
// 是不是一个空对象
// var isEmptyObject = function( obj ) {
// var name;
// // 这种写法不好 for in 不能遍历到 Symbol 的属性
// // 也会遍历到原型链上的属性
// for ( name in obj ) {
// return false;
// }
// return true;
// }
// 我们实现一个优化过的空对象检测方法
var isEmptyObject = function( obj ) {
var keys = Object.keys(obj);
// 看浏览器是否支持 Symbol
if (typeof Symbol !== "undefined") {
keys = keys.concat(Object.getOwnPropertySymbols(obj));
}
return keys.length === 0;
}
// 检测是不是数字 支持 1 '1'
var isNumeric = function( obj ) {
// 获取类型
var type = jQuery.type( obj );
return ( type === "number" || type === "string" ) &&
!isNaN( obj );
};