JQ部分源码分析
(function () {
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); //=>"function Object() { [native code] }"
var getProto = Object.getPrototypeOf; //获取当前对象的原型链__proto__
// 建立数据类型检测的映射表 { "[object Array]":"array",....}
var mapType = ["Boolean", "Number", "String", "Function", "Array", "Date", "RegExp", "Object", "Error", "Symbol", "BigInt"];
mapType.forEach(function (name) {
class2type["[object " + name + "]"] = name.toLocaleLowerCase();
});
// 检测数据类型的办法
var toType = function toType(obj) {
if (obj == null) {
// 传递的是 null/undefined
return 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) {
// typeof obj.nodeType !== "number" :防止在部分浏览器中,检测<object>元素对象结果也是"function",但是它的nodeType=1,处理浏览器兼容问题
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) {
// length存储的是对象的length属性值或者是false
// type存储的是检测的数据类型
var length = !!obj && "length" in obj && obj.length,
type = toType(obj);
// window.length=0 && Function.prototype.length=0
if (isFunction(obj) || isWindow(obj)) return false;
// type === "array" 数组
// length === 0 空的类数组
// 最后一个条件判断的是非空的类数组「有length属性,并且最大索引在对象中」
return type === "array" || length === 0 ||
typeof length === "number" && length > 0 && (length - 1) in obj;
};
// 检测是否为纯粹的对象 例如:{}
var isPlainObject = function isPlainObject(obj) {
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;
};
// 检测是否为空对象
var isEmptyObject = function isEmptyObject(obj) {
// 排除非对象
if (obj == null) return false;
if (typeof obj !== "object") return false;
// 是一个对象「纯粹对象或者特殊对象都可以」
var keys = Object.keys(obj);
if (hasOwn.call(Object, 'getOwnPropertySymbols')) {
// 兼容这个属性的情况下,我们再去拼接
keys = keys.concat(Object.getOwnPropertySymbols(obj));
}
return keys.length === 0;
};
// 检测是否为数字
var isNumeric = function isNumeric(obj) {
var type = toType(obj);
return (type === "number" || type === "string") && !isNaN(+obj);
};
// 暴露到外部
var utils = {
toType: toType,
isFunction: isFunction,
isWindow: isWindow,
isArrayLike: isArrayLike,
isPlainObject: isPlainObject,
isEmptyObject: isEmptyObject,
isNumeric: isNumeric
};
if (typeof window !== "undefined") {
window._ = window.utils = utils;
}
if (typeof module === "object" && typeof module.exports === "object") {
module.exports = utils;
}
})();