ECMAScript 5
在ECMAScript 5中,Object.prototype.toString()被調用時,會進行如下步驟:
- 如果
this
是undefined
,返回object Undefined
; - 如果
this
是null
, 返回object Null
; - 令
O
爲以this
作爲參數調用ToObject
的結果; - 令
class
爲O
的內部屬性[[Class]]
的值; - 返回三個字符串 "[
object"
,class
, 以及"]"
拼接而成的字符串。
[[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)
例:
Object.prototype.toString.call(null)
// => '[object Null]'
Object.prototype.toString.call(undefined)
// => '[object Undefined]'
Object.prototype.toString.call(Math)
// => '[object Math]'
Object.prototype.toString.call({})
// => '[object Object]'
Object.prototype.toString.call([])
// => '[object Array]'
Object.prototype.toString.call(0)
// => '[object Number]'
Object.prototype.toString.call(NaN)
// => '[object Number]'
Object.prototype.toString.call('')
// => '[object String]'
因此,可以用下列函數,來獲取任意變量的[[Class]]
屬性:
function getType (val) {
const _str = Object.prototype.toString.call(val)
return /^\[object (.*)\]$/.exec(_str)[1]
}
運行:
getClass(null)
// => 'Null'
getClass(undefined)
// => 'Undefined'
getClass(Math)
// => 'Math'
getClass({})
// => 'Object'
getClass([])
// => 'Array'
ECMAScript 6
在ES6,調用 Object.prototype.toString
時,會進行如下步驟:
- 如果
this
是undefined
,返回'[object Undefined]'
; - 如果
this
是null
, 返回'[object Null]'
; - 令
O
爲以this
作爲參數調用ToObject
的結果; - 令
isArray
爲IsArray(O)
; ReturnIfAbrupt(isArray)
(如果isArray
不是一個正常值,比如拋出一個錯誤,中斷執行);- 如果
isArray
爲true
, 令builtinTag
爲'Array'
; else
,如果O is an exotic String object
, 令builtinTag
爲'String'
;else
,如果O
含有[[ParameterMap]] internal slo
, , 令builtinTag 爲 'Arguments'
;else
,如果O
含有[[Call]] internal method
, 令builtinTag
爲Function
;else
,如果O
含有[[ErrorData]] internal slot
, 令builtinTag 爲
Error` ;else
,如果O
含有[[BooleanData]] internal slot
, 令builtinTag
爲Boolean
;else
,如果O
含有[[NumberData]] internal slot
, 令builtinTag
爲Number
;else
,如果O
含有[[DateValue]] internal slot
, 令builtinTag 爲 Date
;else
,如果O
含有[[RegExpMatcher]] internal slot
, 令builtinTag
爲RegExp
;else
, 令builtinTag
爲Object
;- 令
tag
爲Get(O, @@toStringTag)
的返回值(Get(O, @@toStringTag
) 方法,既是在O
是一個對象,並且具有@@toStringTag
屬性時,返回O[Symbol.toStringTag]
); ReturnIfAbrupt(tag)
,如果tag
是正常值,繼續執行下一步;- 如果
Type(tag)
不是一個字符串,let tag be builtinTag
; - 返回由三個字符串
"[object", tag, and "]"
拼接而成的一個字符串。
在ES6裏,之前的[[Class]]
不再使用,取而代之的是一系列的 internal slot
,有一個比較完整的解釋:
Internal slots correspond to internal state that is associated with objects and used by various ECMAScript specification algorithms. Internal slots are not object properties and they are not inherited. Depending upon the specific internal slot specification, such state may consist of values of any ECMAScript language type or of specific ECMAScript specification type values
大概的意思是:Internal slots 對應於與對象相關聯並由各種ECMAScript規範算法使用的內部狀態,它們沒有對象屬性,也不能被繼承,根據具體的 Internal slot 規範,這種狀態可以由任何ECMAScript語言類型或特定ECMAScript規範類型值的值組成。
此外,通過對 Object.prototype.toString 在ES6的實現步驟分析,我們其實可以很容易改變 Object.prototype.toString.call 的結果,像下面一樣:
let obj = {}
Object.defineProperty(obj, Symbol.toStringTag, {
get: function() {
return "newClass"
}
})
console.log(Object.prototype.toString.call(obj)) // "[object newClass]"
參考:
- http://www.ecma-international.org/ecma-262/5.1
- http://www.adobe.com/devnet/archive/html5/articles/categorizing-values-in-javascript.html
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString
- http://www.ecma-international.org/ecma-262/6.0/
- http://es6.ruanyifeng.com/#docs/symbol
- https://tc39.github.io/ecma262/#sec-object.prototype.tostring