数据类型检测
主要包括集中常用的数据类型检测方法和JQuery中的数据类型检测
1. typeof
- 返回结果都是字符串
- 字符串中包含了对应的数据类型"number"/"string"/"boolean"/"undefined"/"symbol"/"object"/"function"
- 在判断基本数据类型的时候,可以使用.
- 局限性
- typeof null => "object"
- typeof array => "object"
- null不是对象,它是空对象指针。而这也正是使用typeof操作 符检测null值时会返回"object"的原因。
- 检测数组或者正则等特殊的对象,返回结果都是
"object",所以无法基于typeof判断是数据还是正则
console.log(typeof(1)); //number
console.log(typeof('hello')); //string
console.log(typeof(true)); //boolean
console.log(typeof(undefined)); //undefined
console.log(typeof({})); //object
console.log(typeof(function() {})); //function
console.log(typeof(null)); //object
console.log(typeof []); //=>"object"
console.log(typeof typeof []); //=>"string"、
//多个typeof检测,结果肯定是string
console.log(typeof(a)); // undefined // 未定义的变量
console.log(typeof("s")); // string
console.log(typeof(1)); // number
console.log(typeof(true)); // boolean
console.log(typeof(new String("s"))); // object
console.log(typeof(new Number(1))); // object
console.log(typeof(new Boolean(true))); // object
console.log(typeof(null)); // object // 在 JavaScript 最初的实现中,JavaScript 中的值是由一个表示类型的标签和实际数据值表示的。对象的类型标签是 0。由于 null 代表的是空指针(大多数平台下值为 0x00),因此,null 的类型标签是 0,typeof null 也因此返回 "object"。
console.log(typeof(undefined)); // undefined
console.log(typeof(Symbol())); // symbol
console.log(typeof(Object(Symbol()))); // object
console.log(typeof(1n)); // bigint // ES10(ES2019)新增基本数据类型
console.log(typeof(Object(BigInt(1n)))); // object
console.log(typeof(function() {})); // function
console.log(typeof([])); // object
console.log(typeof(new Date())); // object
console.log(typeof(/regex/)); // object
console.log(typeof({})); // object
2. instanceof
instanceof是用来判断A是否为B的实例,表达式为:A instanceof B,如果A是B的实例,则返回true,否则返回false。- 检测某
实例是否属于这个类 - 他检测的底层机制:所有出现在其原型链上的类,检测结果都是TRUE
- 不能检测基本数据类型,在原型链上的结果未必准确
- 不能检测
null,undefined - 使用
instanceof时对于字面量声明的String、Number、Boolean、Symbol、BigInt都会返回false。 - instanceof主要用来弥补
typeof不能检测具体属于哪个对象的局限性 - 局限性
- 由于可以基于
__proto__或者prototype改动原型链的动向,所以基于instanceof检测出来的结果并不一定是准确的 - 基本数据类型的值,连对象都不是,更没有
__proto__,虽说也是所属类的实例,在JS中也可以调取所属类原型上的方法,但是instanceof是不认的
- 检测不了基本属性类型,检测引用类型虽然不准,但是可以做一个简单的参考。
let arr = [1,2,3];
let reg = /\w/;
console.log(arr instanceof Array); //true
console.log(arr instanceof Object); //true
console.log(reg instanceof RegExp); //true
console.log(reg instanceof Object); //true
function Fn() {}
Fn.prototype.__proto__ = Array.prototype;
let f = new Fn();
//=>原型链:f -> Fn.prototype -> Array.prototype -> Object.prototype
console.log(12 instanceof Number); //=>false
console.log('qwe' instanceof String) //=>false
// instanceof判断一个对象是否是另一个对象的实例,而数字12是基本数据类型,不是对象, string也不是对象
格式是:object instanceof constructor
var a = new Number(1);// 是通过包装类Number把数字1转换成对象,你可以用typeof a,和typeof 1,看看他们返回的值
console.log(new Number(12) instanceof Number); //=>true
console.log([] instanceof Array); //=>true
console.log([] instanceof Object); //=>true
/^$/ instanceof RegExp //true 检测正则
console.log("s" instanceof String); // false
console.log(1 instanceof Number); // false
console.log(true instanceof Boolean); // false
console.log(new String("s") instanceof String); // true
console.log(new Number(1) instanceof Number); // true
console.log(new Boolean(true) instanceof Boolean); // true
console.log(null instanceof Object); // false
console.log(undefined instanceof Object); // false
console.log(Symbol() instanceof Symbol); // false
console.log(Object(Symbol()) instanceof Symbol); // true
console.log(1n instanceof BigInt); // false
console.log(Object(1n) instanceof BigInt); // true
console.log(Symbol() instanceof Symbol); // false
console.log((function() {}) instanceof Function); // true
console.log([] instanceof Array); // true
console.log(new Date() instanceof Date); // true
console.log(/regex/ instanceof RegExp); // true
console.log({} instanceof Object); // true
7. 手写instanceof
- a instanceof b
function myInstanceOf(a,b){
let left = a.__proto__;
let right = b.prototype;
while(true){
if(left == null){ //找到最顶层,还不相等
return false
}
if(left === right){ //严格相等
return true
}
left = left.__proto__ //没找到继续向上一层原型链查找
}
}
//instanceof 运算符用于判断构造函数的 prototype 属性是否出现在对象的原型链中的任何位置。
function myInstanceof(left, right) {
let proto = Object.getPrototypeOf(left), // 获取对象的原型
prototype = right.prototype; // 获取构造函数的 prototype 对象
// 判断构造函数的 prototype 对象是否在对象的原型链上
while (true) {
if (!proto) return false;
if (proto === prototype) return true;
proto = Object.getPrototypeOf(proto);
}
}
3. constructor
- 作用和
instanceof非常相似。 - 要注意一点的是,当直接用(对象字面量或原始数据)
.constructor时,最好加上()
// 基本类型可以用,引用类型不准确
let arr =[]
arr.constructor
arr.constructor === Array //true
12.constructor === Number; //报错,Invalid or unexceped token
(12).constructor
let n =12
n.constructor === Number //true
{}.constructor === Number; //报错,Invalid or unexceped token
({}).constructor === Number; //true
- 局限性:我们可以把类的原型进行重写,在重写的过程中很可能把之前constructor给覆盖了,这样检测出来的结果就是不准确的
Array.property.constructor = null
arr.constructor //null
arr.constructor ===Array //false
4. Object.prototype.toString
Object.prototype.toString.call([value])
- 只有Object上的原型的
toString()才可以检测数据类型。 Object.prototype.toString的作用是返回当前方法的执行主体(方法中的this)所属类的详细信息,是最全面也是最常用的检测数据类型的方式。
Number/String/Boolean/Symbol他们的原型(.propertype)上都有:
=>toString:转化为字符串
=>valueOf:返回原始值
Array/RegExp/Function等内置类的原型上都有
=>toString:转化为字符串
Object的原型上
=>toString:返回当前实例所属类的信息
=>valueOf:返回原始值
- 不是用来转换为字符串的,而是返回当前实例所属类的信息.
格式:"[object 所属类信息]"
"[object Object/Array/RegExp/Date/Function/Null/Undefined/Number/String/Boolean/Symbol...]"
- 这种方式基本上没有什么局限性,是检测数据类型最准确的方式!!!
- 使用方法
Object.prototype.toString.call([value])
// 或者
({}).toString.call([value])
- 举例
({}).toString.call(12) // "[object Number]"
({}).toString.call('12') // "[object String]"
({}).toString.call(true) // "[object Boolean]"
({}).toString.call(null) // "[object Null]"
({}).toString.call(undefined) // "[object Undefined]"
({}).toString.call(Symbol()) // "[object Symbol]"
({}).toString.call([]) // "[object Array]"
({}).toString.call(function (){}) // "[object Function]"
({}).toString.call(/^$/) // "[object RegExp]"
console.log(Object.prototype.toString.call("s")); // [object String]
console.log(Object.prototype.toString.call(1)); // [object Number]
console.log(Object.prototype.toString.call(true)); // [object Boolean]
console.log(Object.prototype.toString.call(new String("s"))); // [object String]
console.log(Object.prototype.toString.call(new Number(1))); // [object Number]
console.log(Object.prototype.toString.call(new Boolean(true))); // [object Boolean]
console.log(Object.prototype.toString.call(null)); // [object Null]
console.log(Object.prototype.toString.call(undefined)); // [object Undefined]
console.log(Object.prototype.toString.call(Symbol())); // [object Symbol]
console.log(Object.prototype.toString.call(Object(Symbol()))); // [object Symbol]
console.log(Object.prototype.toString.call(1n)); // [object BigInt]
console.log(Object.prototype.toString.call(Object(BigInt(1n)))); // [object BigInt]
console.log(Object.prototype.toString.call(function() {})); // [object Function]
console.log(Object.prototype.toString.call([])); // [object Array]
console.log(Object.prototype.toString.call(new Date())); // [object Date]
console.log(Object.prototype.toString.call(/regex/)); // [object RegExp]
console.log(Object.prototype.toString.call({})); // [object Object]
改变原型链,不能影响检测结果
function Fn(){};
let f = new Fn();
({}).toString.call(f) // "[object Object]" 实例是一个对象
function Fn(){};
Fn.prototype = Array.prototype
let f = new Fn();
({}).toString.call(f) // "[object Object]" f还是obj,不会因为原型被修改而改变
为什么用Object.prototype.toString.call(obj)检测对象类型?
JQuery中的数据类型检测
console.log(null == undefined) // true
console.log(null === undefined) // false
- 检测数据类型
var class2type = {};
var toString = class2type.toString; //=>Ob ject.prototype.toString
var hasOwn = class2type.hasOwnProperty; //=>Object.prototype.hasOwnProperty
var fnToString = hasOwn.toString; //=>Function.prototype.toString
var ObjectFunctionString = fnToString.call(Object); //=>Object.toString() =>"function Object() { [native code] }"
"Boolean Number String Function Array Date RegExp Object Error Symbol".split(" ").forEach(function anonymous(item) {
class2type["[object " + item + "]"] = item.toLowerCase();
});
console.log(class2type);
// ******
function toType(obj) {
//=>obj may be null / undefined
//=>return "null"/"undefined"
if (obj == null) {
return obj + "";
}
return typeof obj === "object" || typeof obj === "function" ? class2type[toString.call(obj)] || "object" :
typeof obj;
}
// jQuery.type = toType;
- 检测是否为空对象(重要)
var isEmptyObject = function isEmptyObject(obj) {
var name;
for (name in obj) {
return false; //只要能循环,就是false,非空
}
return true;
};
- 检测是否为数组或类数组(重要)
var isArrayLike = function isArrayLike(obj) {
var length = (!!obj)&& "length" in obj && obj.length, // obj传东西了(不是null undefined),且有length的属性,有的话获取length属性
type = toType(obj); //判断类型
if ( isFunction(obj) || isWindow(obj)) { // 如果传进来是function或者window (window也有length,值为0)
return false;
}
return type === "array" || length === 0 || (typeof length === "number" && length > 0 && (length - 1) in obj); //type为array || 有length属性 || length不是0,且是个数字,且Length > 0, length -1 :最大索引对象是否是数组的元素
};
- 检测是否为函数
var isFunction = function isFunction(obj) {
return typeof obj === "function" && typeof obj.nodeType !== "number";
};
- 检测是否为window对象
// window.window===window
var isWindow = function isWindow(obj) {
return obj != null && obj === obj.window;
};
- 检测是否是纯粹的对象
var isPlainObject = function isPlainObject(obj) {
var proto, Ctor;
if (!obj || toString.call(obj) !== "[object Object]") {
return false;
}
//=>getPrototypeOf获取当前对象的原型
proto = Object.getPrototypeOf(obj);
// Objects with no prototype (`Object.create( null )`)
if (!proto) {
return true;
}
// Objects with prototype are plain iff they were constructed by a global Object function
// 看原型上有没有constructor属性(constructor指向当前类)
Ctor = hasOwn.call(proto, "constructor") && proto.constructor;
return typeof Ctor === "function" && fnToString.call(Ctor) === ObjectFunctionString;
//ObjectFunctionString = fnToString.call(Object); //=>Object.toString() =>"function Object() { [native code] }"
};