类型判断
typeof
typeof
返回操作数的类型名称字符串,它没什么原理不原理的,返回值是直接在规范中给出来的,如下表:
类型 | 结果 |
---|---|
Undefined | "undefined" |
Null | "object" |
Boolean | "boolean" |
Number | "number" |
String | "string" |
Symbol | "symbol" |
BigInt | "bigint" |
Function | "function" |
其它引用类型 | "object" |
class | "function" |
instanceof
obj instanceof Constructor
@return Boolean
过去instanceof
主要用于判断对象的类型,其原理是通过原型链查找obj.__proto__
是否等于类型构造函数的原型Constructor.prototype
function _instanceof(obj, constructor) {
if (obj === null) {
return false;
}
// 判断原型是否存在
if (constructor && constructor.prototype) {
var _prototype = Object.getPrototypeOf
? Object.getPrototypeOf(obj)
: obj.__proto__;
if (_prototype === constructor.prototype) {
return true;
} else {
_instanceof(_prototype, constructor.prototype);
}
}
return false;
}
ES6 以后,通过Symbol.hasInstance
可以定义instanceof
的判断结果,instanceof
在执行的时候会优先获取Symbol.hasInstance
的值,并将其转换称Boolean
类型返回。
class Person {
static [Symbol.hasInstance]() {
return true;
}
}
//这个被改造的类使用instanceof,无论什么都会返回true
console.log(undefined instanceof Person); //true
//判断class是不是使用new调用的
function _instanceof(this, myClass) {
if (typeof Symbol !== 'undefined' && myClass[Symbol.hasInstance]) {
return !!myClass[Symbol.hasInstance](this);
}
}
Object.prototype.toString
根据 ES 规范文档的描述,早期的 ES 规范设计的有一个[[Class]]
属性,用作各种内置对象的标称类型标签,只能通过Object.prototype.toString
这个方法获取[[Class]]
的字符串值。
Object.prototype.toString
是目前为止兼容性最强,类型判断最准确的方式,它连 Promise 都能判断(靓仔狂喜);根据 ES 规范文档的描述,toString.call(this)
被调用会做以下处理:
- 判断 this 是不是
undefined
,是返回"[object Undefined]"
; - 判断 this 是不是
null
,是返回"[object Null]"
; - 对 this 进行
ToObject
转换,得到包装的对象; - 获取包装对象的
Symbol.toStringTag
属性,如果存在并且值是String
类型,则返回其值和字符串"[object"
连接的结果; - 如果不存在
Symbol.toStringTag
属性,且为内置对象,返回类型名称和字符串"[object"
连接的结果; - 如果都不满足,最后返回
[object Object]
var type = Object.prototype.toString.call(window);
console.log(type); //[object Window]
var type = Object.prototype.toString.call(new Date());
console.log(type); //[object Date]
var type = Object.prototype.toString.call(null);
console.log(type); //[object Null]
var type = Object.prototype.toString.call(undefined);
console.log(type); //[object Undefined]
function Foo() {}
var foo = new Foo();
var type = Object.prototype.toString.call(foo);
console.log(type); //[object Object]
可以用正则表达式处理一下结果,只显示类型字符串
function _typeof(obj) {
return Object.prototype.toString
.call(obj)
.replace(/^\[object\s(\w+)\]$/, '$1')
.toLowerCase();
}
从 ES6 开始,规范定义了Symbol.toStringTag
属性,程序中可以直接使用Symbol.toStringTag
为类型定义名称,执行Object.prototype.toString
的过程也发生了变化,会优先根据Symbol.toStringTag
属性进行返回值的判断,如果没有再根据对象的私有属性判断其类型。
Array.prototype[Symbol.toStringTag] = 'test';
console.log(Object.prototype.toString.call(new Array())); // [object test]
Symbol.toStringTag
ES6 新增的内置对象的Symbol.toStringTag
属性值可以返回内置对象类型的字符串,它返回的内容是规范定义的,并不是调用Object.prototype.toString
这个方法,注意不能搞混淆了。
JSON[Symbol.toStringTag]
:'JSON'Math[Symbol.toStringTag]
:'Math'- Module 对象
M[Symbol.toStringTag]
:'Module' ArrayBuffer.prototype[Symbol.toStringTag]
:'ArrayBuffer'DataView.prototype[Symbol.toStringTag]
:'DataView'Map.prototype[Symbol.toStringTag]
:'Map'Promise.prototype[Symbol.toStringTag]
:'Promise'Set.prototype[Symbol.toStringTag]
:'Set'%TypedArray%.prototype[Symbol.toStringTag]
:'Uint8Array'等WeakMap.prototype[Symbol.toStringTag]
:'WeakMap'WeakSet.prototype[Symbol.toStringTag]
:'WeakSet'%MapIteratorPrototype%[Symbol.toStringTag]
:'Map Iterator'%SetIteratorPrototype%[Symbol.toStringTag]
:'Set Iterator'%StringIteratorPrototype%[Symbol.toStringTag]
:'String Iterator'Symbol.prototype[Symbol.toStringTag]
:'Symbol'Generator.prototype[Symbol.toStringTag]
:'Generator'GeneratorFunction.prototype[Symbol.toStringTag]
:'GeneratorFunction'
不过这里单独要讲一下Date
类型,如果直接访问一个Date
类型对象的Symbol.toStringTag
属性,得到的结果是undefined
,这里是因为规范根本没有给Date
类型定义Symbol.toStringTag
这个属性,可以参考这个回答 —— 为什么 Date 的 Symbol.toStringTag 返回的是 undefined ? - 知乎 (zhihu.com)
如果是自定义的对象类型,使用这个属性为创建的类定义类型名称,弥补toString
的不足。
class Person {
get [Symbol.toStringTag]() {
return 'Person';
}
}
var o = new Person();
console.log(Object.prototype.toString.call(o)); //[object Person]
Object()
当Object
作为函数单独调用时,实际上发生的就是装箱的ToObject
操作,将除了Null
和Undefined
类型外的原始值包装成对象,但是对于引用类型的Object
,它都是直接返回这个值,所以可以利用Object
和===
判断一个值是不是引用类型。
Object(obj) === obj; //true
其它类型内置的判断方法
Array.isArray()
判断数组
Array.isArray()
其实也是利用了toString
的实现方式
if (!Array.isArray) {
Array.isArray = function(arg) {
return Object.prototype.toString.call(arg) === '[object Array]';
};
}
判断对象是否为引用类型
function isObject(obj) {
if (obj !== null && (typeof obj === 'object' || typeof obj === 'function')) {
return true;
}
return false;
}