使用 toString() 检测对象
可以通过 toString() 来获取每个对象的类型。为了每个对象都能通过 Object.prototype.toString() 来检测,需要以 Function.prototype.call() 或者 Function.prototype.apply() 的形式来调用,传递要检查的对象作为第一个参数,称为 thisArg。
var toString = Object.prototype.toString;
toString.call(new Date); // [object Date]
toString.call(new String); // [object String]
toString.call(Math); // [object Math]
//Since JavaScript 1.8.5
toString.call(undefined); // [object Undefined]
toString.call(null); // [object Null]
检测原理
Object.prototype.toString.call(obj) 类型检测的原理是什么?首先我们来看一下 toString() 方法:
var num = 1
console.log(num.toString()) // '1'
var str = 'kevin'
console.log(str.toString()) // 'kevin'
var bool = false
console.log(bool.toString()) // 'false'
var arr = [1, 2, 3]
console.log(arr.toString()) // '1,2,3'
var obj = { name: 'kevin' }
console.log(obj.toString()) // '[object Object]'
var fn = function(){}
console.log(fn.toString()) // 'function(){}'
console.log(JSON.toString()) // '[object JSON]'
console.log(Atomics.toString()) // '[object Atomics]'
console.log(null.toString()) // Cannot read property 'toString' of null
console.log(undefined.toString() // Cannot read property 'toString' of undefined
console.log(window.toString()) // '[object Window]'
从以上示例可以知道 toString 是将数据转换为字符串(null 和 undefined 除外),并且各种类型的数据转换为字符串的方式又不一样。即若参数不为 null 或 undefined,则将参数转为对象,再作判断。对于原始类型,转为对象的方法即装箱。
转为对象后,取得该对象的 [Symbol.toStringTag] 属性值(可能会遍历原型链)作为 tag,如无该属性,或该属性值不为字符串类型,则依下表取得 tag,然后返回 "[object " + tag + "]" 形式的字符串。
新标准引入了 [Symbol.toStringTag] 属性,是为了把此方法接口化,用于规范新引入的对象对此方法的调用。但对于“老旧”的对象,就只能直接输出值,以保证兼容性。
// 1. 三个容器对象。这类对象用作命名空间,用于存储同一类方法。
JSON[Symbol.toStringTag]; // => "JSON"
Math[Symbol.toStringTag]; // => "Math"
Atomics[Symbol.toStringTag]; // => "Atomic"
// 这三个对象的 toString() 都没有重写,直接调用 toString() 方法也可以得到相同的结果。
JSON.toString(); // => "[object JSON]"
Math.toString(); // => "[object Math]"
Atomics.toString(); // => "[object Atomics]"
// 2. 两个新引入的类型 BigInt 和 Symbol。
BigInt.prototype[Symbol.toStringTag]; // => "BigInt"
Symbol.prototype[Symbol.toStringTag]; // => "Symbol"
// 3. 四个集合(Collection)对象。
Set.prototype[Symbol.toStringTag]; // => "Set"
Map.prototype[Symbol.toStringTag]; // => "Map"
WeakSet.prototype[Symbol.toStringTag]; // => "WeakSet"
WeakMap.prototype[Symbol.toStringTag]; // => "WeakMap"
// 4. 在不同的实现中,有些第三方对象也部署了此属性。
// 比如在浏览器中:
Window.prototype[Symbol.toStringTag]; // => "Window"
HTMLElement.prototype[Symbol.toStringTag]; // => "HTMLElement"
Blob.prototype[Symbol.toStringTag]; // => "Blob"
// 5. 模块命名空间对象(Module Namespace Object)。
// 新引入的模块命名空间对象(Module Namespace Object)也是部署了此属性的。
import * as module from "./export.js";
module[Symbol.toStringTag]; // => "Moduel"
// 6. 在 Node.js 中
global[Symbol.toStringTag]; // => "global"