常见类型判断
typeof,instanceof,constructor,Object.prototype.toString.call()有什么不同?让你知道各种边界,玩转类型判断
基本数据类型判断
typeof 操作符
主要用于JS中基本数据类型(值类型)的判断,返回值有string, number, bigint, boolean, undefined, symbol, object, function 八个值
console.log(typeof 42);
// output: "number"
console.log(typeof 'blubber');
// output: "string"
console.log(typeof true);
// output: "boolean"
console.log(typeof undeclaredVariable);
// output: "undefined"
function Car(){}
console.log(typeof new Car());
// output: "object" 无法检测自定义类型
// 注意:⚠️
typeof null === 'object'; // true
null == undefined // true
null === undefined // false
引用数据类型判断
构造函数、原型和实例的关系:每个
构造函数都有一个原型对象(prototype属性),原型对象有一个属性(constructor属性)指回构造函数,而该构造函数创建的实例有一个内部指针[[Prototype]]/__proto__指向原型对象prototype ————来自JavaScript高级程序设计
1. Constructor
直接判断构造函数
//1. 基本数据类型
var num = 1;
num.constructor === Number //true
''.constructor === String // true
true.constructor === Boolean //true
// 2. 原生构造函数
new Date().constructor === Date // 原声true
new Error().constructor === Error //true
[].constructor === Array //true
document.constructor === HTMLDocument //true
// 自定义
function Car(){}
(new Car()).constructor === Car; //true
// 3. null | undefined 报错
null.constructor //Uncaught TypeError: Cannot read properties of null (reading 'constructor')
undefined.constructor //Uncaught TypeError: Cannot read properties of undefined (reading 'constructor')
2. instanceof 操作符
instance instanceof constructorFun 检测构造函数原型,也就是constructorFun.prototype是否出现在该实例对象instance的原型链上
[] instanceof Array; //true
{} instanceof Object; //true
[] instanceof Object //true 继承
new Date() instanceof Date;//true
new Date() instanceof Object;//true 继承
(1) 可以检测自定义类型
function Car(){}
console.log(new Car() instanceof Car); //true
console.log((new Car()) instanceof Object) //继承
(2) 不能检测js基本数据类型
var num1 = 123;
num1 instanceof Number; //false :不能检测js基本数据类型
var num2 = new Number(123);
num instanceof Number; //true
// 注意:⚠️
null instanceof Object //false
undefined instanceof Object// false
原始值包装类型123与引用类型new Number(123)的区别
详见(javascript的高级程序设计5.3 原始值包装类型)介绍,摘抄如下:
引用类型与原始值包装类型的主要区别在于对象的生命周期。用下面的例子理解:
let s1 = "some text";
s1.color = "red"; //临时创建一个 String 对象,后销毁
console.log(s1.color); // undefined 创建了自己的 String 对象,但这个对象没有 color 属性
instanceof的欺骗行为案例
function foo() {}
const bar = { a: 'a'};
foo.prototype = bar; //串改foo.prototype
console.log(bar instanceof foo); // false:bar的原型链上只有Object.prototype,foo.prototype不在bar的原型链上
const baz = Object.create(bar); // 使用bar作为原型,创建baz对象
console.log(baz instanceof foo); // true:baz的原型是bar,而foo.prototype = bar,所以相等
instanceof的本质理解
下面例子理解object instanceof Constructor,instanceof 运算符的检测本质是用来检测 constructor.prototype 是否存在于参数 object 的原型链上,注意⚠️:所有引用类型都继承自 Object
- Object.getPrototypeOf(obj): 获取obj的原型
- prototypeObj.isPrototypeOf(targetObj)
: 测试
prototypeObj是否存在于targetObj的原型链上
// 定义构造函数
function C(){}
function D(){}
var o = new C(); // o的原型链C.prototype => Object.prototype
o instanceof C; // true o的原型指向C.prototype
Object.getPrototypeOf(o) === C.prototype
o instanceof D; // false,因为 D.prototype 不在 o 的原型链上
o instanceof Object; // true
Object.prototype.isPrototypeOf(o) //true
C.prototype instanceof Object // true
C.prototype = {};
var o2 = new C(); //o2的原型链 {} => Object.prototype
o2 instanceof C; // true
o instanceof C; // false,C.prototype 指向了一个空对象,这个空对象不在 o 的原型链上。
D.prototype = new C(); // 继承
var o3 = new D(); // o3的原型链 D.prototype =>C实例 => {} => Object.prototype
o3 instanceof D; // true
o3 instanceof C; // true 因为 C.prototype 现在在 o3 的原型链上
3. Object.prototype.toString.call()
- JS内置的对象类型返回
[object Type] - 只支持原生的类型,对于自定义的类型永远是
[object Object]
Object.prototype.toString.call('') ; // [object String]
Object.prototype.toString.call(1) ; // [object Number]
Object.prototype.toString.call(true) ; // [object Boolean]
Object.prototype.toString.call(Symbol()); //[object Symbol]
Object.prototype.toString.call(undefined) ; // [object Undefined]
Object.prototype.toString.call(null) ; // [object Null]
Object.prototype.toString.call(new Function()) ; // [object Function]
Object.prototype.toString.call(new Date()) ; // [object Date]
Object.prototype.toString.call([]) ; // [object Array]
Object.prototype.toString.call(new RegExp()) ; // [object RegExp]
Object.prototype.toString.call(new Error()) ; // [object Error]
Object.prototype.toString.call(document) ; // [object HTMLDocument]
//自定义的类型永远是`[object Object]`
function Car(){}
console.log(Object.prototype.toString.call(new Car())); //[object Object]
总结
- 基本数据类型用
typeof,要注意typeof null === 'object';为true - 需要判断是否在原型链上用
instanceof注意⚠️:undefined | null instanceof Object为false constructor直接判断构造函数,在自定义类型上,要担心开发者重写 prototype 后,可能原有的 constructor 引用会丢失,注意⚠️:undefined | null.constructor报错Object.prototype.toString.call(value)只支持JS的原生对象类型,返回[object Type]
例子
关于React中Redux里面的isPlainObject的写法如下所示:
- Plain Object:纯粹的对象(通过 "{}" 或者 "new Object" 创建的)
export default function isPlainObject(obj: any): boolean {
// 用typeof,注意了null的区别
if (typeof obj !== 'object' || obj === null) return false
// 寻找原型链的顶端
let proto = obj
while (Object.getPrototypeOf(proto) !== null) {
proto = Object.getPrototypeOf(proto)
}
return Object.getPrototypeOf(obj) === proto
}
其他常用场景
判断NaN Number.isNaN() VS isNaN()
isNaN(value)会对value进行转换后再判断 Eg:isNaN("123ABC") // true=》 因为Number("123ABC")是NaN(隐式转换)- 推荐使用
Number.isNaN("123ABC") // false不会对值进行隐式转换
Number(); //0
Number(undefined); // NaN
Number(""); // 0
Number(null); // 0
Number(true); // 1
Number(false); //0
Number("12.00"); // 12
数组检测 Array.isArray(value)
推荐使用,解决了instanceof Array不同iframes检测不了的情况,详见
检查对象是否为空
const isEmptyObj = (value)=>value instanceof Object && Reflect.ownKeys(value).length === 0 && value.constructor === Object;
console.log(isEmptyObj({}))//true
console.log(isEmptyObj([]))//false
console.log(isEmptyObj(1))//false
console.log(isEmptyObj(""))//false
console.log(isEmptyObj(null))//false
console.log(isEmptyObj(undefined))//false