_.isSymbol(value)
检查value是否是原始Symbol或者对象。
返回true or false。
对Symbol不太了解的同学可以参考我的这篇文章----> ES6之Symbol
自己实现
function isSymbol(value) {
return typeof value === 'symbol'
}
源码
function isSymbol(value) {
return typeof value == 'symbol' ||
(isObjectLike(value) && baseGetTag(value) == symbolTag)
}
分析
为什么使用==?
首先有个疑问,为什么typeof value == 'symbol'用的是双等号而不用严格相等===呢?
typeof返回值的类型是字符串,那么这里其实应该是严格相等的,而且用==是会进行隐式的类型转换的,这里没有必要。从规范上来说,也应该是尽量减少==的使用,使用===。个人认为,这里应该使用===。
为什么后续还要进行类对象的判断?
(isObjectLike(value) && baseGetTag(value) == symbolTag)
先看isObjectLike(value),顾名思义就是检查value是否是类对象。如果一个值是类对象,那么它不应该是null,而且typeof后的结果是"object"。具体的实现和源码可以参考我的这篇文章中的isObjectLike部分---->lodash源码系列——isObject和isObjectLike
再看symbolTag的定义:
var symbolTag = '[object Symbol]'
可以看出,当value为类对象,并且baseGetTag(value)的返回值为'[object Symbol]'时,isSymbol返回true。
那么,为什么要进行这些后续的类对象的判断?原来是因为,创建一个显示包装对象从ES6开始不再被支持了,现在可以用如下代码来模拟:
let objSym = Object(Symbol())
typeof objSym // "object"
_.isSymbol(objSym) // true
objSym的类型是object,但是_.isSymbol()返回的却是true。对类对象的检测就是检测的这种情况。
baseGetTag方法
由上面可以看出,baseGetTag这个方法主要用来获取value的类型标签。
源码:
function baseGetTag(value) {
if (value == null) {
return value === undefined ? undefinedTag : nullTag
}
return (symToStringTag && symToStringTag in Object(value)) ?
getRawTag(value) :
objectToString(value)
}
前面已经对value进行了isObjectLike的判断,故此处其实无需对value==null再进行判断,个人认为这么做的原因其实是因为这是一个lodash的内部方法,这个方法独立来看的话还是需要进行判断的。
undefinedTag、nullTag:
var undefinedTag = '[object Undefined]'
var nullTag = '[object Null]'
故当value为null或undefined时,返回的是'[object Undefined]'或'[object Null]'
否则,当value!= null时,继续往下看:
return (symToStringTag && symToStringTag in Object(value)) ?
getRawTag(value) :
objectToString(value)
symToStringTag定义为:
var symToStringTag = Symbol ? Symbol.toStringTag : undefined;
Symbol.toStringTag是一个内置symbol,它通常作为对象的属性键使用,对应的属性值应该为字符串类型,这个字符串用来表示该对象的自定义类型标签。具体的内容可以参考我的另一篇文章---->Symbol的toStringTag属性
当前环境支持Symbol,则symToStringTag为Symbol.toStringTag,否则为undefined。
objectToString方法
symToStringTag=undefined时,对value调用objectToString方法:
function objectToString(value) {
return nativeObjectToString.call(value);
}
其实调用的是nativeObjectToString:
var nativeObjectToString = objectProto.toString;
objectProto:
var objectProto = Object.prototype
所以归根结底,objectToString方法就是Object.prototype.toString.call(value)。
也就是说,当对象没有Symbol.toStringTag属性时,可以通过调用toString()返回特定的类型标签。
symToStringTag=Symbol.toStringTag时,并且Object(value)不存在symToStringTag属性时,仍然调用objectToString用来返回value的类型标签。
getRawTag方法
symToStringTag=Symbol.toStringTag时,并且Object(value)存在symToStringTag属性时,对value调用getRawTag方法。
function getRawTag(value) {
// 判断symToStringTag是否为类对象value自身具有的属性
var isOwn = hasOwnProperty.call(value, symToStringTag),
// 取出类对象value的symToStringTag属性值赋值给tag
tag = value[symToStringTag];
try {
// 修改value的symToStringTag属性对应的值为undefined
value[symToStringTag] = undefined;
var unmasked = true;
} catch (e) {}
// result为对value调用toString()的返回值
var result = nativeObjectToString.call(value);
if (unmasked) {
// symToStringTag是value自身就具有的属性
if (isOwn) {
// 修改value的symToStringTag属性对应的值为tag
value[symToStringTag] = tag;
}
// symToStringTag不是value自身就具有的属性
else {
delete value[symToStringTag];
}
}
return result;
}
归根结底,返回的结果还是对value调用toString()方法的返回值。
总结
当一个value的类型是'symbol',或者类型是'object'但是toString()的返回值为'[object Symbol]'时,_.isSymbol(value)返回true,否则返回false。