题目要求
写一个返回数据类型的函数,如果自定义的类实例化的对象返回定义的类名
为什么不直接使用typeof判断
CAUTION
typeof null会等于object,值得注意的是typeof (null)会返回Object,这是因为JS二进制前三位都为0的话会被判断为Object类型,null的二进制表示是全0,自然前三位也是0,所以执行typeof时会返回Object,实际null为基本数据类型。 而且对于 对象和数组来说,都会转换成
object 对于
null,我们可以使用===来进行判断 所以,
typeof可以判断基本数据类型,但是难以判断除了函数以外的复杂数据类型
typeof 1 // 'number'typeof '1' // 'string'typeof null // 'object'typeof undefined // 'undefined'
typeof [] // 'object'typeof {} // 'object'typeof function(){} // 'function'instanceof是否能正确判断类型
WARNING
instanceof是通过原型链来判断的,但是对于对象来说,Array也会被转换成Object,而且也不能区分基本类型string和boolean。
function Func() {} const func = new Func() console.log(func instanceof Func) // true
const obj = {} const arr = [] obj instanceof Object // true arr instanceof Object // true arr instanceof Array // true
const str = "abc" const str2 = new String("abc") str instanceof String // false str2 instanceof String // true基于以上结论,先完成一个简易的函数:
function myTypeof(data) { const type = typeof data if(data === null) return 'null' if(type !== 'object') return type if(data instanceof Array) return 'array' return 'object'}constructor
TIP constructor 判断方法跟
instanceof相似,但是constructor检测Object与instanceof不一样,constructor还可以处理基本数据类型的检测,不仅仅是对象类型。注意:
- null和undefined没有constructor;
- 判断数字时使用(),比如 (123).constructor,如果写成
123.constructor会报错- constructor在类继承时会出错,因为Object被覆盖掉了,检测结果就不对了:
// 注意当出现继承的时候,使用constructor会出现问题function A() {}function B() {}A.prototype = new B() // A继承自Bconsole.log(A.constructor === B) // falsevar C = new A()console.log(C.constructor === A) // falseconsole.log(C.constructor === B) // true//解决这种情况,通常是手动调整对象的constructor指向C.constructor = Aconsole.log(C.constructor === A) // trueconsole.log(C.constructor === B) // falseArray.isArray()
Array.isArray([1, 2, 3]); // true Array.isArray({foo: 123}); // false Array.isArray("foobar"); // false Array.isArray(undefined); // false正则判断
TIP 我们可以把对象和数组转成一个字符串,这样就可以做格式判断,从而得到最终的类型。
function myTypeof(data) { const str = JSON.stringify(data) if(/^{.*}$/.test(str)){ return 'object' } if(/^\[.*\]$/.test(str)){ return 'array' }}Object.prototype.toString.call()
TIP 之所以要这样调用
toString方法,在之前的文章中已经提到过,是为了确保调用的是Object原型上的toString方法 每个对象都有一个
toString()方法,当要将对象表示为文本值或以预期字符串的方式引用对象时,会自动调用该方法。默认情况下,从Object派生的每个对象都会继承toString()方法。如果此方法未在自定义对象中被覆盖,则toString()返回[Object type],其中type是对象类型。所以就有以下例子:
Object.prototype.toString.call(new Date()) // [object Date] Object.prototype.toString.call("1") // [object String] Object.prototype.toString.call(1) // [object Numer] Object.prototype.toString.call(undefined) // [object Undefined] Object.prototype.toString.call(null) // [object Null]综上,封装出以下通用类型的判断方法:
function myTypeof(data){ var toString = Object.prototype.toString var dataType = data instanceof Element ? 'Element' : toString.call(data).replace(/\[object\s(.+)\]/,"$1") return dataType}获取实例化对象的类名
TIP 我们只需要对,上述获取的
Object类型的数据,直接使用xx.constructor.name即可
最终实现💯
function myTypeof(data) { var toString = Object.prototype.toString; var dataType = data instanceof Element ? "Element" : toString.call(data).replace(/\[object\s(.+)\]/, "$1")
if(dataType === 'Object'){ return data.constructor.name }
return dataType};