我们先复习一下js的数据类型。
JavaScript 的数据类型包括原始类型和对象类型:
- 原始类型:Null、Undefined、Number、String、Boolean、Symbol、BigInt
- 对象类型:Object
我们习惯把对象称为引用类型,当然还有很多特殊的引用类型,比如 Function、Array、RegExp、Math、Date、Error、Set、Map、各种定型数组 TypedArray 等。
JavaScript 中检测数据类型的方法有哪些你知道吗?
- typeof
- instanceof
- constructor
- Object.prototype.toString.call()
typeof
typeof 'a' // 'string'
typeof 1 // 'number'
typeof true // 'boolean'
typeof undefined // 'undefined'
typeof Symbol('a') // 'symbol'
typeof 1n // 'bigint'
typeof null // 'object'
typeof function() {} // 'function'
typeof [] // 'object'
typeof {} // 'object'
typeof /a/ // 'object'
typeof new Date() // 'object'
typeof new Error() // 'object'
typeof new Map() // 'object'
typeof new Set() // 'object'
两条结论:
- typeof 可以判断除了 null 以外的原始类型。
- typeof 只能判断对象类型中的 Function,其他判断不出来,都为 object。
instanceof
console.log(1 instanceof Number) // false
console.log(new Number(1) instanceof Number) // true
const arr = []
console.log(arr instanceof Array) // true
console.log(arr instanceof Object) // true
const Fn = function() {
this.name = '构造函数'
}
Fn.prototype = Object.create(Array.prototype)
let a = new Fn()
console.log(a instanceof Array) // true
两条结论:
instanceof
可以准确判断对象(引用)类型,但是不能准确检测原始类型。- 由于我们可以随意修改原型的指向导致检测结果不准确,所以这种方法是不安全的。
虽然 instanceof
不能检测原始类型,但是有一种方法可以让其用于检测原始类型。
Symbol.hasInstance
允许我们自定义 instanceof
的行为。
class PrimitiveNumber {
static [Symbol.hasInstance] = x => typeof x === 'number';
}
123 instanceof PrimitiveNumber; // true
class PrimitiveString {
static [Symbol.hasInstance] = x => typeof x === 'string';
}
'abc' instanceof PrimitiveString; // true
class PrimitiveBoolean {
static [Symbol.hasInstance] = x => typeof x === 'boolean';
}
false instanceof PrimitiveBoolean; // true
class PrimitiveSymbol {
static [Symbol.hasInstance] = x => typeof x === 'symbol';
}
Symbol.iterator instanceof PrimitiveSymbol; // true
class PrimitiveNull {
static [Symbol.hasInstance] = x => x === null;
}
null instanceof PrimitiveNull; // true
class PrimitiveUndefined {
static [Symbol.hasInstance] = x => x === undefined;
}
undefined instanceof PrimitiveUndefined; // true
代码来源: 有没有一种方法可以将instanceof用于原始JavaScript值?
constructor
// 所以需要加上一个小括号,小括号运算符能够把数值转换为对象
(1).constructor // ƒ Number() { [native code] }
// 或者
1..constructor // ƒ Number() { [native code] }
const a = '前端食堂'
console.log(a.constructor) // ƒ String() { [native code] }
console.log(a.constructor === String) // true
const b = 5
console.log(b.constructor) // ƒ Number() { [native code] }
console.log(b.constructor === Number) // true
const c = true
console.log(c.constructor) // ƒ Boolean() { [native code] }
console.log(c.constructor === Boolean) // true
const d = []
console.log(d.constructor) // ƒ Array() { [native code] }
console.log(d.constructor === Array) // true
const e = {}
console.log(e.constructor) // ƒ Object() { [native code] }
console.log(e.constructor === Object) // true
const f = () => 1
console.log(f.constructor) // ƒ Function() { [native code] }
console.log(f.constructor === Function) // true
const g = Symbol('1')
console.log(g.constructor) // ƒ Symbol() { [native code] }
console.log(g.constructor === Symbol) // true
const h = new Date()
console.log(h.constructor) // ƒ Date() { [native code] }
console.log(h.constructor === Date) // true
const i = 11n
console.log(i.constructor) // ƒ BigInt() { [native code] }
console.log(i.constructor === BigInt) // true
const j = /a/
console.log(j.constructor) // ƒ RegExp() { [native code] }
console.log(j.constructor === RegExp) // true
String.prototype.constructor = 'aaa'
console.log(a.constructor === String) // false
const k = null
console.log(k.constructor) // Cannot read property 'constructor' of null
const l = undefined
console.log(l.constructor) // Cannot read property 'constructor' of undefined
两条结论:
- 除了 null 和 undefined,
constructor
可以正确检测出原始类型和对象(引用)类型。 - 由于我们可以随意修改
constructor
导致检测结果不准确,所以这种方法是不安全的。
Object.prototype.toString.call()
toString() 方法返回一个表示该对象的字符串,我们可以改变它的 this 指向,将 this 指向要检测的值,即可返回当前检测值的信息。
Object.prototype.toString({}) // '[object Object]'
Object.prototype.toString.call({}) // '[object Object]'
Object.prototype.toString.call('a') // '[object String]'
Object.prototype.toString.call(1) // '[object Number]'
Object.prototype.toString.call(true) // '[object Boolean]'
Object.prototype.toString.call(null) // '[object Null]'
Object.prototype.toString.call(undefined) // '[object Undefined]'
Object.prototype.toString.call(Symbol('a')) // '[object Symbol]'
Object.prototype.toString.call(11n) // '[object BigInt]'
Object.prototype.toString.call(/a/) // '[object RegExp]'
Object.prototype.toString.call(new Date()) // '[object Date]'
Object.prototype.toString.call([0, 1, 2]) // '[object Array]'
Object.prototype.toString.call(function() {}) // '[object Function]'
Object.prototype.toString.call(new Error()) // '[object Error]'
Object.prototype.toString.call(new Set()) // '[object Set]'
Object.prototype.toString.call(new Map()) // '[object Map]'
ps:文末附上汇总文章链接《一名【合格】前端工程师的自检清单【自检ing】》