typeof
返回一个字符串,包含了对应的数据类型。
typeofnull//objecttypeofnewNumber(10)//object//typeof不能细分对象//非null的原始值和函数类型的检测很友好
原理:根据计算机底层存储值的二进制来计算。性能会好一些。
对象类型监测不友好,基础类型除了null都可以快速检测。
以000
开始的值都被认为是对象类型,而null的值是000000
,所以被认为是对象。
Function 类型判断是否实现了call 方法,有就是function 类型
instanceof
本意不是检测数据类型,而是检测当前实例是不是属于这个类。
可以根据instanceof 细分对象类型。
[]instanceofArray//true[]instanceofObject//true
原理:首先按照构造函数的
Symbol.hasInstance([实例])
方法进行检测,如果存在这个属性方法,则方法执行返回的值就是最后检测结果。如果不存在这个属性方法,则会查找当前实例的原型链(一直找到Object.prototype为止),如果查找中途,找到了某个原型等于构造函数的原型「构造函数出现在实例的原型链上」则返回结果为true,反之是false。
缺陷:
无法检测原始值类型(不会像隐式调用一样有装箱操作)
如果原型重定向后此方法就无法正确找到原型
10instanceofNumber//falsefunctionFn(){}Fn.prototype=[];letf=newFn();finstanceofArray//true
//es6可以重写Symbol.hasInstance方法,es5不可以classFn{static[Symbol.hasInstance](value){returntrue;}}1instanceofFn//true
functioninstance_of(obj,Ctor){//数据格式校验if(Ctor==null||Ctor==undefined){thrownewTypeError('Right-handsideof"instanceof"isnotanobject');};if(!Ctor.prototype){thrownewTypeError('Malformedarrowfunctionparameterlist');}if(typeofCtor!=='function'){thrownewTypeError('Right-handsideof"instanceof"isnotcallable');}//原始值类型忽略if(obj==null)returnfalse;if(!/^(object|function)$/.test(typeofobj))returnfalse;//检测是否有Ctor[Symbol.hasInstance]方法if(typeofCtor[Symbol.hasInstance]==='function'){returnCtor[Symbol.hasInstance](obj);}//走到最后按照原型链查找letprototype=Object.getPrototypeOf(obj);while(prototype){if(prototype===Ctor.prototype)returntrue;prototype=Object.getPrototypeOf(prototype);}returnfalse;}
constructor
一个获取对象的构造函数,不是用来专门判断数据类型的。
优点:
为true的值至多只有一个
原始值调用时会进行“装箱”(除了 null 和 undefined)
缺点
constructor是可以被随意更改的,检测结果不一定准确
[].constructor===Array//true[].constructor===Object//false
Object.prototype.toString.call()
最为推荐的一种方案。
原理:首先找到Object.prototype.toString方法,把toString执行之后,让方法中的this变为要检测的值,toString内部会返回对应this的数据类型信息。
返回值:'[object Object]'
大部分值所属类的原型上都有toString方法,除了Object.prototype.toString 是用来检测类型的,其余的一般都是用来转为字符串的。
非普通对象.toString 一般先调取自己所属类原型上的toString
。
普通对象.toString 会直接调取Object.prototype.toString
。
({}).toString()//[objectObject]Object.prototype.toString.call({})//[objectObject](1).toString()//'1'
步骤:首先看[value] [Symbol.toStringTag],如果存在这个属性,属性值是啥,返回的类型就是啥,如果没有这个属性,一般是返回所属的构造函数信息
不同场景下的类型检测
varclass2type={};//ObjectvargetProto=Object.getPrototypeOf;vartoString=class2type.toString;//Object.prototype.toStringvarhasOwn=class2type.hasOwnProperty;//Object.prototype.hasOwnPropertyvarfnToString=hasOwn.toString;//Function.prototype.toString//ObjectFunctionString='functionObject(){[nativecode]}'varObjectFunctionString=fnToString.call(Object);//Object.toString()vartypeArr=['Boolean','Number','String','Function','Array','Date','RegExp','Object','Error','Symbol','BigInt','Map','Set'];//...typeArr.forEach(function(name){class2type["[object"+name+"]"]=name.toLowerCase();})functiontoType(obj){if(obj==null){returnobj+"";}returntypeofobj==="object"||typeofobj==="function"?class2type[toString.call(obj)]||"object":typeofobj;}//使用正则functiontoType(obj){if(obj==null){returnobj+"";}if(typeofobj!=="object"||typeofobj!=="function"){returntypeofobj;}varreg=/^[object([0-9A-Za-z]+)]$/,value=reg.exec(toString.call(obj))[1]||'object';returnvalue.toLowerCase();}//是否是一个函数functionisFunction(obj){//JS中获取object元素,在某些浏览器中,基于typeof检测这个对象,返回的是functionreturntypeofobj==="function"&&typeofobj.nodeType!=="number"&&typeofobj.item!=="function";};//是否是window对象functionisWindow(obj){//undefined==null->truereturnobj!=null&&obj===obj.window;};//检测是否为数组和类数组functionisArrayLike(obj){varlength=!!obj&&"length"inobj&&obj.length,type=toType(obj);if(isFunction(obj)||isWindow(obj)){returnfalse;}returntype==="array"||length===0||typeoflength==="number"&&length>0&&(length-1)inobj;}//检测是否为存粹对象(直属类是Object,数组这类不是)functionisPlainObject(obj){varproto,Ctor;if(!obj||toType(obj)!=='object'){returnfalse;}proto=getProto(obj);//Objectswithnoprototype(e.g.,`Object.create(null)`)areplainif(!proto){returntrue;}//ObjectswithprototypeareplainifftheywereconstructedbyaglobalObjectfunctionCtor=hasOwn.call(proto,"constructor")&&proto.constructor;returntypeofCtor==="function"&&fnToString.call(Ctor)===ObjectFunctionString;}//检测是否是空对象functionisEmptyObject(obj){if(obj==null){returnfalse;}//Object.getOwnPropertyNames可以获取不可枚举的keyObject.keys获取可枚举的keyvarkeys=Object.getOwnPropertyNames(obj);if(typeofSymbol!=='undefined'){keys=keys.concat(Object.getOwnPropertySymbols(obj));}returnkeys.length===0;}//是否是数字functionisNumeric(obj){vartype=toType(obj);return(type==="number"||type==="string")&&!isNaN(obj-parseFloat(obj));};