Skip to content

Latest commit

 

History

History
136 lines (92 loc) · 4.55 KB

hasPath.md

File metadata and controls

136 lines (92 loc) · 4.55 KB

lodash源码分析之hasPath

本文为读 lodash 源码的第三百七十三篇,后续文章会更新到这个仓库中,欢迎 star:pocket-lodash

gitbook也会同步仓库的更新,gitbook地址:pocket-lodash

依赖

import castPath from './.internal/castPath.js'
import isArguments from './isArguments.js'
import isIndex from './.internal/isIndex.js'
import isLength from './isLength.js'
import toKey from './.internal/toKey.js'

《lodash源码分析之castPath》

《lodash源码分析之isArguments》

《lodash源码分析之isIndex》

《lodash源码分析之isLength》

《lodash源码分析之toKey》

源码分析

hasPath 用于检测 object 上是否存在路径 path

如:

const object = { 'a': { 'b': 2 } }
hasPath(object, 'a.b') // => true
hasPath(object, ['a', 'b']) // => true

源码如下:

const hasOwnProperty = Object.prototype.hasOwnProperty
function hasPath(object, path) {
  path = castPath(path, object)

  let index = -1
  let { length } = path
  let result = false
  let key

  while (++index < length) {
    key = toKey(path[index])
    if (!(result = object != null && hasOwnProperty.call(object, key))) {
      break
    }
    object = object[key]
  }
  if (result || ++index != length) {
    return result
  }
  length = object == null ? 0 : object.length
  return !!length && isLength(length) && isIndex(key, length) &&
    (Array.isArray(object) || isArguments(object))
}

先是调用 castPath 将路径都转换成路径数组。

while (++index < length) {
  key = toKey(path[index])
  if (!(result = object != null && hasOwnProperty.call(object, key))) {
    break
  }
  object = object[key]
}

接着遍历路径,每次遍历都使用 toKey 来确保当前的值为合法的属性名。

可以看到,每次遍历都会将当前的属性值从上层 object 上取出,重新赋值给 object

因此要确保 keyobject 的属性,则 object 不能为 nullundefined ,如果为这两者,则下一级属性无法取值。

其次,属性 key 还要为当前 object 自身的属性,即能通过 Object.hasOwnProperty 的检测。

如果在遍历的过程中,还满足这两个条件,则结果 result 会被置为 false ,中止遍历。

if (result || ++index != length) {
  return result
}

在经过上述的遍历后,会得到结果 result ,如果最终的结果 resulttrue ,则遍历肯定已经完成,中间没有出现过 resultfalse 的情况,也即属性路径 pathobject 上。

如果 resultfalse ,并且 ++index != length ,表示属性路径没有遍历完成,也即 while 循环中途退出了,那肯定属性路径 path 不在 object 上,也直接返回结果 false 即可。

除了以上两种情况外,还有一种情况,就是遍历完成了,但是 resultfalse ,这时需要进行下面的判断:

length = object == null ? 0 : object.length
return !!length && isLength(length) && isIndex(key, length) &&
  (Array.isArray(object) || isArguments(object))

这种是什么情况呢?这种情况发生在稀疏数组,或者 arguments 上。

例如:

const a = new Array(3)
hasPath(a, '0') // => true

在这种情况下,先判断 object == null ,如果为 null 或者 undefined ,则不是数组,则将 length 设置为 0 ,下面可以看到,这种情况是返回 false 的。否则从 object 上取出 length 属性。

先是判断 !!length ,如果 length0 ,则数组上没有任何元素,返回 false

接着判断 length 是否为合法的数组长度,如果不是,也返回 false

接着判断 key 是否为 index 索引,使用 isIndex 来判断,确保 key 的值比 length 要小。

在上述判断完成后,再判断 object 是否为数组或者 arguments 对象。

在满足上述条件后,返回 true

License

署名-非商业性使用-禁止演绎 4.0 国际 (CC BY-NC-ND 4.0)

最后,所有文章都会同步发送到微信公众号上,欢迎关注,欢迎提意见:

作者:对角另一面