题目要求
写一个返回数据类型的函数,如果自定义的类实例化的对象返回定义的类名
为什么不直接使用typeof
判断
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继承自B
console.log(A.constructor === B) // false
var C = new A()
console.log(C.constructor === A) // false
console.log(C.constructor === B) // true
//解决这种情况,通常是手动调整对象的constructor指向
C.constructor = A
console.log(C.constructor === A) // true
console.log(C.constructor === B) // false
Array.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
即可
最终实现:100:
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
};