全部的悲伤,总会留下一丝欢乐的线索,全部的遗憾,总会留下一处完美的角落,我在冰峰的深海,寻找但愿的缺口,却在惊醒时,瞥见绝美的阳光!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)
最后,全部文章都会同步发送到微信公众号上,欢迎关注,欢迎提意见:
做者:对角另外一面