深入理解Object.prototype.toString方法
ECMAScript 3
在toString方法被调用时,会执行下面的操作步骤:
-
获取this对象的[[Class]]属性的值.(第一步)
-
计算出三个字符串"[object ", 第一步的操作结果Result(1), 以及 "]"连接后的新字符串.(第二步)
-
返回第二步的操作结果Result(2). (第三步)
[[Class]]是一个内部属性,所有的对象(原生对象和宿主对象)都拥有该属性.在规范中,[[Class]]是这么定义的
[[Class]] 一个字符串值,表明了该对象的类型.
所有内置对象的[[Class]]属性的值是由本规范定义的.
所有宿主对象的[[Class]]属性的值可以是任意值,甚至可以是内置对象使用过的[[Class]]属性的值.
[[Class]]属性的值可以用来判断一个原生对象属于哪种内置类型.需要注意的是,除了通过Object.prototype.toString方法之外,本规范没有提供任何其他方式来让程序访问该属性的值。
ECMAScript 5
在ECMAScript 5中,Object.prototype.toString()被调用时,会进行如下步骤:
1 如果 this是undefined ,返回 [object Undefined] ;
2 如果 this是null , 返回 [object Null] ;
3 令 O 为以 this 作为参数调用 ToObject 的结果;
4 令 class 为 O 的内部属性 [[Class]] 的值;
5 返回三个字符串 “[object”, class, 以及"]" 拼接而成的字符串。
[[Class]]是一个内部属性,值为一个类型字符串,可以用来判断值的类型。
有这么一段详细的解释:
本规范的每种内置对象都定义了 [[Class]] 内部属性的值。宿主对象的 [[Class]] 内部属性的值可以是除了 "Arguments", "Array", "Boolean", "Date", "Error", "Function", "JSON", "Math", "Number", "Object", "RegExp", "String" 的任何字符串。[[Class]] 内部属性的值用于内部区分对象的种类。注,本规范中除了通过 Object.prototype.toString ( 见 15.2.4.2) 没有提供任何手段使程序访问此值。
在JavaScript代码里,唯一可以访问该属性的方法就是通过 Object.prototype.toString ,通常方法如下:
Object.prototype.toString.call(value)
可以用下列函数,来获取任意变量的[[Class]]属性:
function getClass (a) {
const str = Object.prototype.toString.call(a)
return /^\[object (.*)\]$/.exec(str)[1]
}
实例如下:
数据类型 | 例子 | 返回值 |
---|---|---|
字符串 | “foo”.toString() | “foo” |
数字 | 1.toString() | Uncaught SyntaxError: Invalid or unexpected token |
布尔值 | false.toString() | “false” |
undefined | undefined.toString() | Uncaught TypeError: Cannot read property ‘toString’ of undefined |
null | null.toString() | Uncaught TypeError: Cannot read property ‘toString’ of null |
String | String.toString() | “function String() { [native code] }” |
Number | Number.toString() | “function Number() { [native code] }” |
Boolean | Boolean.toString() | “function Boolean() { [native code] }” |
Array | Array.toString() | “function Array() { [native code] }” |
Function | Function.toString() | “function Function() { [native code] }” |
Date | Date.toString() | “function Date() { [native code] }” |
RegExp | RegExp.toString() | “function RegExp() { [native code] }” |
Error | Error.toString() | “function Error() { [native code] }” |
Promise | Promise.toString() | “function Promise() { [native code] }” |
Obejct | Object.toString() | “function Object() { [native code] }” |
Math | Math.toString() | “[object Math]” |
ECMAScript 6
1 在ES6,调用 Object.prototype.toString 时,会进行如下步骤:
2 如果 this 是 undefined ,返回 ‘[object Undefined]’ ; (函数直接返回分支)
3 如果 this 是 null , 返回 ‘[object Null]’ ;(函数直接返回分支)
4 令 O 为以 this 作为参数调用 ToObject 的结果;(调用ToObject函数获取结果O)
5 令 isArray 为 IsArray(O) ;(调用IsArray(O) 函数获取结果赋值给isArray )
6 ReturnIfAbrupt(isArray) (如果 isArray 不是一个正常值,比如抛出一个错误,中断执行);
7 如果 isArray 为 true , 令 builtinTag 为 ‘Array’ ;
8 else ,如果 O is an exotic String object , 令 builtinTag 为 ‘String’ ;
9 else ,如果 O 含有 [[ParameterMap]] internal slot, , 令 builtinTag 为 ‘Arguments’ ;
10 else ,如果 O 含有 [[Call]] internal method , 令 builtinTag 为 Function ;
11 else ,如果 O 含有 [[ErrorData]] internal slot , 令 builtinTag 为 Error ;
12 else ,如果 O 含有 [[BooleanData]] internal slot , 令 builtinTag 为 Boolean ;
13 else ,如果 O 含有 [[NumberData]] internal slot , 令 builtinTag 为 Number ;
14 else ,如果 O 含有 [[DateValue]] internal slot , 令 builtinTag 为 Date ;
15 else ,如果 O 含有 [[RegExpMatcher]] internal slot , 令 builtinTag 为 RegExp ;
16 else , 令 builtinTag 为 Object ;
17 令 tag 为 Get(O, @@toStringTag) 的返回值( Get(O, @@toStringTag) 方法,既是在 O 是一个对象,并且具有 @@toStringTag 属性时,返回 O[Symbol.toStringTag] );
18 ReturnIfAbrupt(tag) ,如果 tag 是正常值,继续执行下一步;
如果 Type(tag) 不是一个字符串,let tag be builtinTag ;
19 返回由三个字符串 “[object”, tag, “]” 拼接而成的一个字符串。
注意:
1 步骤1,2是Object.prototype.toString(null)和Object.prototype.toString(undefined )的特殊入参的处理
2 步骤7到16是获取builtinTag变量的值;
3 步骤17的解释:
let obj = {}
Object.defineProperty(obj, Symbol.toStringTag, {
get: function() {
return "newClass"
}
})
console.log(Object.prototype.toString.call(obj)) // "[object newClass]"
ECMAScript 7
ES7目前还是工作草案,到目前为止,就 Object.prototype.toString 的实现步骤来说, 只是移除了ES6其中的 ReturnIfAbrupt 。
附言
Object对象和它的原型链上各自有一个toString()方法,第一个返回的是一个函数,第二个返回的是值类型。
Object.toString.call(Array)//"function Array() { [native code] }"
Object.prototype.toString.call(Array)//"[object Function]"
Object.toString()//"function Object() { [native code] }"
Object.prototype.toString()//"[object Object]"