跳到主要内容

类型判断

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

ECMA - 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()

ToObject ( argument )

Object作为函数单独调用时,实际上发生的就是装箱的ToObject操作,将除了NullUndefined类型外的原始值包装成对象,但是对于引用类型的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;
}