对于 Object.prototype.toString()
方法,会返回一个形如 "[object XXX]"
的字符串。
如果对象的 toString
方法没有被重写,就会返回如上面形式的字符串。
({}).toString(); // => "[object Object]"
Math.toString(); // => "[object Math]"
但是,大部分对象的 toString
方法被重写了,这时,需要用 call()
或者 Reflect.apply()
等方法来调用。
// 验证是否是数组
function isArray(obj) {
return Object.prototype.toString.call(obj) === '[object Array]'
}
isArray([1,2,3]) // true
isArray('1') // false
Object.prototype.toString 原理
对于 Object.prototype.toString.call(arg)
,若参数为 null
或 undefined
,直接返回结果。
Object.prototype.toString.call(null); // => "[object Null]"
Object.prototype.toString.call(undefined); // => "[object Undefined]"
若参数不为 null
或 undefined
,则将参数转为对象,再做判断。
转为对象后,取得该对象的 [Symbol.toStringTag]
属性值(可能会遍历原型链)作为 tag
,如无该属性,或该属性不为基本类型,则按照下面规则 取 tag值,然后返回 [object,tag]类型的字符串。
注意: Symbol.toStringTag 的作用是可以指定 toString方法的tag值,具体在基础9:元编程里面有说明。
// Boolean 类型,tag 为 "Boolean"
Object.prototype.toString.call(true); // => "[object Boolean]"
// Number 类型,tag 为 "Number"
Object.prototype.toString.call(1); // => "[object Number]"
// String 类型,tag 为 "String"
Object.prototype.toString.call(""); // => "[object String]"
// Array 类型,tag 为 "String"
Object.prototype.toString.call([]); // => "[object Array]"
// Arguments 类型,tag 为 "Arguments"
Object.prototype.toString.call((function() {
return arguments;
})()); // => "[object Arguments]"
// Function 类型, tag 为 "Function"
Object.prototype.toString.call(function(){}); // => "[object Function]"
// Error 类型(包含子类型),tag 为 "Error"
Object.prototype.toString.call(new Error()); // => "[object Error]"
// RegExp 类型,tag 为 "RegExp"
Object.prototype.toString.call(/\d+/); // => "[object RegExp]"
// Date 类型,tag 为 "Date"
Object.prototype.toString.call(new Date()); // => "[object Date]"
// 其他类型,tag 为 "Object"
Object.prototype.toString.call(new class {}); // => "[object Object]"
下面为部署了 Symbol.toStringTag
的例子。可以看出,属性值期望是一个字符串,否则会被忽略。
var o1 = { [Symbol.toStringTag]: "A" };
var o2 = { [Symbol.toStringTag]: null };
Object.prototype.toString.call(o1); // => "[object A]"
Object.prototype.toString.call(o2); // => "[object Object]"
Symbol.toStringTag
也可以部署在原型链上:
class A {}
A.prototype[Symbol.toStringTag] = "A";
a = new A()
Object.prototype.toString.call(a); // => "[object A]"
部署了 Symbol.toStringTag 属性的内置对象
以下,是部署了此属性的内置对象。
1. 三个容器对象
JSON[Symbol.toStringTag]; // => "JSON"
Math[Symbol.toStringTag]; // => "Math"
Atomics[Symbol.toStringTag]; // => "Atomic"
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. ArrayBuffer
及其视图对象
ArrayBuffer.prototype[Symbol.toStringTag]; // => "ArrayBuffer"
SharedArrayBuffer.prototype[Symbol.toStringTag]; // => "SharedArrayBuffer"
DataView.prototype[Symbol.toStringTag]; // => "DataView"
5. 模块命名空间对象(Module Namespace Object)
import * as module from "./export.js";
module[Symbol.toStringTag]; // => "Moduel"
6. 其他
浏览器中:
Window.prototype[Symbol.toStringTag]; // => "Window"
HTMLElement.prototype[Symbol.toStringTag]; // => "HTMLElement"
Blob.prototype[Symbol.toStringTag]; // => "Blob"
node中:
global[Symbol.toStringTag]; // => "global"
检测数组的优化
优化前:
// 验证是否是数组
function isArray(obj) {
return Object.prototype.toString.call(obj) === '[object Array]'
}
isArray([1,2,3]) // true
isArray('1') // false
优化后:
var toStr = Function.prototype.call.bind(Object.prototype.toString)
function isArray(obj) {
return toStr(obj) === '[object Array]'
}
isArray([1,2,3]) // true
isArray('1') // false
toStr([1, 2, 3]); // "[object Array]"
toStr("123"); // "[object String]"
toStr(123); // "[object Number]"
toStr(Object(123)); // "[object Number]"
其实,Function.prototype.call.bind(Object.prototype.toString)
就相当于,Object.prototype.toString.call()
,其中 toStr 的方法的第一个参数为传入的参数。