談談 Object.prototype.toString 。

原文連接個人blogjavascript

前幾日看到一個比較熟悉的面試題,判斷一個變量是否是數組?
如下幾種方法供參考:html

var arr = [1, 2, 3]
Array.isArray(arr)
arr instanceof Array
arr.constructor === Array
Object.prototype.toString.call(arr) === '[object Array]'
...複製代碼

這篇文章主要是談談 Object.prototype.toStringhtml5

ECMAScript 5

在ECMAScript 5中,Object.prototype.toString()被調用時,會進行以下步驟:java

  • 若是 thisundefined ,返回 [object Undefined]
  • 若是 thisnull , 返回 [object Null]
  • O 爲以 this 做爲參數調用 ToObject 的結果;
  • classO 的內部屬性 [[Class]] 的值;
  • 返回三個字符串 "[object", class, 以及"]" 拼接而成的字符串。

[[Class]]

[[Class]]是一個內部屬性,值爲一個類型字符串,能夠用來判斷值的類型。git

有這麼一段詳細的解釋:es6

本規範的每種內置對象都定義了 [[Class]] 內部屬性的值。宿主對象的 [[Class]] 內部屬性的值能夠是除了 "Arguments", "Array", "Boolean", "Date", "Error", "Function", "JSON", "Math", "Number", "Object", "RegExp", "String" 的任何字符串。[[Class]] 內部屬性的值用於內部區分對象的種類。注,本規範中除了經過 Object.prototype.toString ( 見 15.2.4.2) 沒有提供任何手段使程序訪問此值。github

在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]'複製代碼

所以,能夠用下列函數,來獲取任意變量的[[Class]]屬性:數組

function getClass (a) {
  const str = Object.prototype.toString.call(a)
  return /^\[object (.*)\]$/.exec(str)[1]
}複製代碼

運行便可得

> getClass(null)
'Null'

> getClass(undefined)
'Undefined'

> getClass(Math)
'Math'

> getClass({})
'Object'

> getClass([])
'Array'複製代碼

ECMAScript 6

在ES6,調用 Object.prototype.toString 時,會進行以下步驟:

  • 若是 thisundefined ,返回 '[object Undefined]' ;
  • 若是 thisnull , 返回 '[object Null]'
  • O 爲以 this 做爲參數調用 ToObject 的結果;
  • isArrayIsArray(O)
  • ReturnIfAbrupt(isArray) (若是 isArray 不是一個正常值,好比拋出一個錯誤,中斷執行);
  • 若是 isArraytrue , 令 builtinTag'Array' ;
  • else ,若是 O is an exotic String object , 令 builtinTag'String'
  • else ,若是 O 含有 [[ParameterMap]] internal slot, , 令 builtinTag'Arguments'
  • else ,若是 O 含有 [[Call]] internal method , 令 builtinTagFunction
  • else ,若是 O 含有 [[ErrorData]] internal slot , 令 builtinTagError
  • else ,若是 O 含有 [[BooleanData]] internal slot , 令 builtinTagBoolean
  • else ,若是 O 含有 [[NumberData]] internal slot , 令 builtinTagNumber
  • else ,若是 O 含有 [[DateValue]] internal slot , 令 builtinTagDate
  • else ,若是 O 含有 [[RegExpMatcher]] internal slot , 令 builtinTagRegExp
  • else , 令 builtinTagObject
  • tagGet(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]"複製代碼

ECMAScript 7

ES7目前仍是工做草案,到目前爲止,就 Object.prototype.toString 的實現步驟來講, 只是移除了ES6其中的 ReturnIfAbrupt

參考

  1. www.ecma-international.org/ecma-262/5.…
  2. www.adobe.com/devnet/arch…
  3. developer.mozilla.org/en-US/docs/…
  4. www.ecma-international.org/ecma-262/6.…
  5. es6.ruanyifeng.com/#docs/symbo…
  6. tc39.github.io/ecma262/#se…

完。

相關文章
相關標籤/搜索