最终函数
function getType(value) {
if (value === null) { return 'null' } // 处理 null 的特殊情况
const basicType = typeof value
if (basicType !== 'object') { return basicType } // 处理基本数据类型
if (value instanceof Date) { return 'date' } // 处理日期对象
if (value instanceof RegExp) { return 'regexp' } // 处理正则表达式对象
if (Array.isArray(value)) { return 'array' } // 处理数组
const objectType = Object.prototype.toString.call(value)
return objectType.slice(8, -1).toLowerCase() // 处理其他对象
}
// 测试
getType(123) // number
getType(1.23) // number
getType(NaN) // number
getType(Infinity) // number
getType('demo1') // string
getType("demo2") // string
getType(`demo3`) // string
getType(true) // boolean
getType(false) // boolean
getType(null) // null
getType(undefined) // undefined
getType(Symbol(123)) // symbol
getType(Symbol('demo')) // symbol
getType(123n) // bigint
getType(()=>{})) // function
getType(function(){}) // function
getType({}) // object
getType([]) // array
getType(new Date()) // date
getType(new RegExp('123')) // regexp
getType(/123/g) // regexp
typeof
typeof 运算符返回一个字符串,表示操作数的类型。
对于原始类型来说,除了 null 都可以显示正确的类型;对于对象类型来说,除了函数都会显示 object。
PS1: 在 JavaScript 最初的实现中,JavaScript 中的值是由一个表示类型的标签和实际数据值表示的。对象的类型标签是 0。由于 null 代表的是空指针(大多数平台下值为 0x00),因此,null 的类型标签是 0,typeof null 也因此返回 "object"。
PS2:使用 typeof 时,加空格(不加括号)和加括号在功能上没有本质区别,但在语法和语义上存在一些细微差异。加空格(不加括号)是 typeof 最常见的使用方式,直接跟要检测的变量或值。加括号的形式类似于函数调用,但 typeof 不是函数,只是语法上看起来像函数调用,括号的作用是将括号内的表达式视为一个整体,先计算表达式的值,再对该值进行类型检测
typeof(123) // number --> 加括号
typeof 123 // number --> 加空格(不加括号)
typeof 1.23 // number
typeof NaN // number
typeof Infinity // number
typeof 'demo1' // string
typeof "demo2" // string
typeof true // boolean
typeof false // boolean
typeof null // object --> null 代表的是空指针, 类型标签是 0
typeof undefined // undefined
typeof Symbol(123) // symbol
typeof Symbol('demo') // symbol
typeof 123n // bigint
typeof (()=>{}) // function
typeof (function(){}) // function
typeof {} // object
typeof [] // object
typeof new Date() // object
typeof new RegExp('123') // object
typeof /123/g // object
instanceof
instanceof 运算符用于检测构造函数的 prototype 属性(即constructor.prototype)是否出现在某个实例对象(object)的原型链上。
instanceof 可以正确的判断对象的类型,因为内部机制是通过判断对象的原型链中是不是能找到类型的 prototype,即判断一个对象是否是某一数据类型的实例。instanceof 可以精准判断引用数据类型(Array,Function,Object),而基本数据类型不能被instanceof精准判断。
123 instanceof Number // false
1.23 instanceof Number // false
NaN instanceof Number // false
Infinity instanceof Number // false
'demo1' instanceof String // false
"demo2" instanceof String // false
true instanceof Boolean // false
false instanceof Boolean // false
null instanceof Null // ReferenceError: Null is not defined
undefined instanceof Undefined // ReferenceError: Undefined is not defined
Symbol(123) instanceof Symbol // false
Symbol('demo') instanceof Symbol // false
123n instanceof BigInt // false
(()=>{}) instanceof Function // true
(function(){}) instanceof Function // true
({}) instanceof Object // true
([]) instanceof Array // true
new Date() instanceof Date // true
new RegExp('123') instanceof RegExp // true
/123/g instanceof RegExp // true
constructor
Object 实例的 constructor 数据属性返回一个引用,指向创建该实例对象的构造函数。注意,此属性的值是对函数本身的引用,而不是一个包含函数名称的字符串。
可以通过访问对象的 constructor 属性,再和特定的构造函数进行比较,从而判断对象的类型。
(123).constructor === Number // true
(1.23).constructor === Number // true
NaN.constructor === Number // true
Infinity.constructor === Number // true
'demo1'.constructor === String // true
"demo2".constructor === String // true
true.constructor === Boolean // true
false.constructor === Boolean // true
null.constructor === Null // TypeError: Cannot read properties of null (reading 'constructor')
undefined.constructor === Undefined // TypeError: Cannot read properties of undefined (reading 'constructor')
Symbol(123).constructor === Symbol // true
Symbol('demo').constructor === Symbol // true
123n.constructor === BigInt // true
(()=>{}).constructor === Function // true
(function(){}).constructor === Function // true
{}.constructor === Object // true
[].constructor === Array // true
new Date().constructor === Date // true
new RegExp('123').constructor === RegExp // true
/123/g.constructor === RegExp // true
constructor 属性可被修改:constructor 属性是可以被修改的,一旦被修改,基于 constructor 的类型判断就会失效。
function Person(name) {
this.name = name
}
const person = new Person('John')
person.constructor = Array
console.log(person.constructor === Person) // 输出: false
Object.prototype.toString.call()
toString() 方法返回一个表示该对象的字符串。该方法旨在重写(自定义)派生类对象的类型转换的逻辑。
使用 Object 对象的原型方法 toString ,使用 call 进行狸猫换太子。
var toStr = Object.prototype.toString
toStr.call(123) // [object Number]
toStr.call(1.23) // [object Number]
toStr.call(NaN) // [object Number]
toStr.call(Infinity) // [object Number]
toStr.call('demo1') // [object String]
toStr.call("demo2") // [object String]
toStr.call(true) // [object Boolean]
toStr.call(false) // [object Boolean]
toStr.call(null) // [object Null]
toStr.call(undefined) // [object Undefined]
toStr.call(Symbol(123)) // [object Symbol]
toStr.call(Symbol('demo')) // [object Symbol]
toStr.call(123n) // [object BigInt]
toStr.call(()=>{}) // [object Function]
toStr.call(function(){}) // [object Function]
toStr.call({}) // [object Object]
toStr.call([]) // [object Array]
toStr.call(new Date()) // [object Date]
toStr.call(new RegExp('123')) // [object RegExp]
toStr.call(/123/g) // [object RegExp]
toString 也不是完全可靠的。
对象可以通过定义 Symbol.toStringTag 属性来更改 Object.prototype.toString() 的行为,从而导致意想不到的结果。
const myDate = new Date();
Object.prototype.toString.call(myDate) // [object Date]
myDate[Symbol.toStringTag] = "myDate"
Object.prototype.toString.call(myDate) // [object myDate]
Date.prototype[Symbol.toStringTag] = "prototype polluted"
Object.prototype.toString.call(new Date()) // [object prototype polluted]