全部的悲傷,總會留下一絲歡樂的線索,全部的遺憾,總會留下一處完美的角落,我在冰峯的深海,尋找但願的缺口,卻在驚醒時,瞥見絕美的陽光!javascript
——幾米java
本文爲讀 lodash 源碼的第十八篇,後續文章會更新到這個倉庫中,歡迎 star:pocket-lodashgit
gitbook也會同步倉庫的更新,gitbook地址:pocket-lodashes6
咱們都知道,能夠借用 Object
原型上的 toString
方法來獲取數據的類型。 baseGetTag
利用的也是這一特性,其返回的結果如 [object String]
這樣的形式,調用方式以下:github
baseGetTag('string') // [object String]
先看 es5
規範對 Object.prototyep.toString
的運行步驟規定:瀏覽器
當調用 toString 方法,採用以下步驟:微信
- 若是 this 的值是 undefined, 返回 "[object Undefined]".
- 若是 this 的值是 null, 返回 "[object Null]".
- 令 O 爲以 this 做爲參數調用 ToObject 的結果 .
- 令 class 爲 O 的 [[Class]] 內部屬性的值 .
- 返回三個字符串 "[object ", class, and "]" 連起來的字符串 .
在第三步的時候,會調用 ToObject
來轉換成對象,而轉換成對象後,會有個 [[Class]]
的內部屬性,而這個內部屬性的值正是 toString
的關鍵部分。源碼分析
接下來再看規範對 [[Class]]
的規定:ui
本規範的每種內置對象都定義了 [[Class]] 內部屬性的值。宿主對象的 [[Class]] 內部屬性的值能夠是除了 "Arguments", "Array", "Boolean", "Date", "Error", "Function", "JSON", "Math", "Number", "Object", "RegExp", "String" 的任何字符串。[[Class]] 內部屬性的值用於內部區分對象的種類。注,本規範中除了經過 Object.prototype.toString ( 見 15.2.4.2) 沒有提供任何手段使程序訪問此值。this
由規範可見,要獲取這個 [[Class]]
內部屬性的值的惟一手段是經過 Object.prototype.toString
。
源碼以下:
const objectProto = Object.prototype const hasOwnProperty = objectProto.hasOwnProperty const toString = objectProto.toString const symToStringTag = typeof Symbol != 'undefined' ? Symbol.toStringTag : undefined function baseGetTag(value) { if (value == null) { return value === undefined ? '[object Undefined]' : '[object Null]' } if (!(symToStringTag && symToStringTag in Object(value))) { return toString.call(value) } const isOwn = hasOwnProperty.call(value, symToStringTag) const tag = value[symToStringTag] let unmasked = false try { value[symToStringTag] = undefined unmasked = true } catch (e) {} const result = toString.call(value) if (unmasked) { if (isOwn) { value[symToStringTag] = tag } else { delete value[symToStringTag] } } return result } export default baseGetTag
在 ES6
中,規範對 Object.prototype.toString
的步驟進行了從新定義,再也不使用 [[Class]]
的內部屬性進行獲取,具體的規範以下:
在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 slot,
, 令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 "]"
拼接而成的一個字符串。
規範對類型的判斷進行了細化,前15步能夠當作跟 es5
的做用同樣,獲取到數據的類型 builtinTag
,可是第16步調用了 @@toStringTag
的方法,若是再看規範的描述,能夠知道這個實際上是對象中的 Symbol.toStringTag
屬性,若是這個屬性返回的是一個字符串,則採用這個返回值 tag
做爲數據的類型,不然才採用 builtinTag
。
if (value == null) { return value === undefined ? '[object Undefined]' : '[object Null]' }
這裏是處理瀏覽器兼容性,在 es5
以前,並無對 null
和 undefined
進行處理,因此返回的都是 [object Object]
。
if (!(symToStringTag && symToStringTag in Object(value))) { return toString.call(value) }
若是瀏覽器不支持 Symbol
或者 value
並不存在 Symbol.toStringTag
的方法,則能夠直接調用 toString
,將結果返回了。
const isOwn = hasOwnProperty.call(value, symToStringTag) const tag = value[symToStringTag] let unmasked = false try { value[symToStringTag] = undefined unmasked = true } catch (e) {} const result = toString.call(value) if (unmasked) { if (isOwn) { value[symToStringTag] = tag } else { delete value[symToStringTag] } }
爲了不 Symbol.toStringTag
的影響,先將 value
的 Symbol.toStringTag
設置爲 undefined
,這樣能夠屏蔽掉原型鏈上的 Symbol.toStringTag
屬性,而後再使用 toString
方法獲取到 value
的屬性描述。
在獲取到屬性描述後,若是 Symbol.toStringTag
爲自身的屬性(不爲原型鏈上的屬性),則將原來保存下來的 tag
從新賦值,不然將 Symbol.toStringTag
屬性移除。
談談 Object.prototype.toString 。
署名-非商業性使用-禁止演繹 4.0 國際 (CC BY-NC-ND 4.0)
最後,全部文章都會同步發送到微信公衆號上,歡迎關注,歡迎提意見:
做者:對角另外一面