lodash源码分析之isKey
本文为读 lodash 源码的第六十五篇,后续文章会更新到这个仓库中,欢迎 star:pocket-lodash
gitbook也会同步仓库的更新,gitbook地址:pocket-lodash
依赖
import isSymbol from '../isSymbol.js'
源码分析
isKey
的作用是用来判断传入的 value
是否为合法的属性名(注意:不是规范中认定的合法属性名,lodash
会做一些收敛),但是在检测的时候会将 lodash
支持的嵌套属性的写法排除,如传入 a.b.c
或者 a[0].b.c
可能会返回 false
。
源码如下:
const reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/
const reIsPlainProp = /^\w*$/
function isKey(value, object) {
if (Array.isArray(value)) {
return false
}
const type = typeof value
if (type === 'number' || type === 'boolean' || value == null || isSymbol(value)) {
return true
}
return reIsPlainProp.test(value) || !reIsDeepProp.test(value) ||
(object != null && value in Object(object))
}
排除数组
if (Array.isArray(value)) {
return false
}
如果传入的是数组,在 lodash
中,不会当作是属性名,直接排除,返回 false
。但其实我们是可以用数组作为 key
的,用数组作为 key
时,数组会首先会转换成 string
类型,再作为对象的属性。 在 lodash
的 isKey
函数中,不允许这种行为。
常规属性名
const type = typeof value
if (type === 'number' || type === 'boolean' || value == null || isSymbol(value)) {
return true
}
接下来,检测 value
是否为数字、布尔值、null
值和是否为 symbol
类型,上述类型,除了 symbol
外,作为属性值时,都会隐式转换成 string
类型,也可以作为合法的属性名。而 symbol
在 es6
中,本身就允许作为对象的属性,不需要转换成 string
类型。
处理字符串类型
return reIsPlainProp.test(value) || !reIsDeepProp.test(value) ||
(object != null && value in Object(object))
现在还剩下字符串和对象没有处理,但是以上的条件中,对象也会隐式转换成字符串来处理,所以这里看字符串的处理即可。
处理单词
const reIsPlainProp = /^\w*$/
reIsPlainProp.test(value)
\w
是匹配a-z、A-Z、0-9,以及下划线的单词字符,resIsPlainProp
是用来匹配包含一个或多个这些字符的单词,也即类似于 a1
、ab
、a-b
这类的字符串,如果是这些字符串,则返回 true
。
处理属性路径
const reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/
!reIsDeepProp.test(value)
在 lodash
中,像 a.b.c
和 a[0].b.c
这种可能会被当作属性路径对待,像 get
这些的函数支持传入这样的路径来获取嵌套对象的值。
reIsDeepProp
这个正则比较复杂,主要就是用来匹配这种深层属性路径的,在 isKey
检测时,会将这些字符串排除。
除此之外的字符串,返回的都是 true
。
本来有这个条件就可以,为什么在这个条件之前还要有 reIsPlainProp.test(value)
这个条件呢?
因为这个正则比较复杂,性能会比较差,所以先匹配 reIsPlainProp
在大部分场景下性能会更加好。
处理属性路径的特例
如果在某些场景中,真的要用属性路径来作为属性名怎么办呢?
这种情况下,使用 isKey
函数时,就需要传入第二个参数 object
了。
object != null && value in Object(object)
如果有传 object
,并且 value
是 object
的属性名,也会返回 true
。这就解决了属性路径作为属性名的问题。
License
署名-非商业性使用-禁止演绎 4.0 国际 (CC BY-NC-ND 4.0)
最后,所有文章都会同步发送到微信公众号上,欢迎关注,欢迎提意见:
作者:对角另一面