Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lodash源码分析之arrayLikeKeys #130

Open
HeftyKoo opened this issue Mar 21, 2020 · 0 comments
Open

lodash源码分析之arrayLikeKeys #130

HeftyKoo opened this issue Mar 21, 2020 · 0 comments
Labels
internal 内部函数或方法 系列文章

Comments

@HeftyKoo
Copy link
Owner

HeftyKoo commented Mar 21, 2020

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

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

依赖

import isArguments from '../isArguments.js'
import isBuffer from '../isBuffer.js'
import isIndex from './isIndex.js'
import isTypedArray from '../isTypedArray.js'

《lodash源码分析之isArguments》
《lodash源码分析之isBuffer》
《lodash源码分析之isIndex》
《lodash源码分析之isTypedArray》

源码分析

arrayLikeKeys 的作用是将 valuekey 收集在一个数组中,作为结果返回。如果是 arrayargumentsbuffertypedArray 等类数组类型,则还会收集索引。

源码如下:

const hasOwnProperty = Object.prototype.hasOwnProperty
function arrayLikeKeys(value, inherited) {
  const isArr = Array.isArray(value)
  const isArg = !isArr && isArguments(value)
  const isBuff = !isArr && !isArg && isBuffer(value)
  const isType = !isArr && !isArg && !isBuff && isTypedArray(value)
  const skipIndexes = isArr || isArg || isBuff || isType
  const length = value.length
  const result = new Array(skipIndexes ? length : 0)
  let index = skipIndexes ? -1 : length
  while (++index < length) {
    result[index] = `${index}`
  }
  for (const key in value) {
    if ((inherited || hasOwnProperty.call(value, key)) &&
        !(skipIndexes && (
        // Safari 9 has enumerable `arguments.length` in strict mode.
          (key === 'length' ||
           // Skip index properties.
           isIndex(key, length))
        ))) {
      result.push(key)
    }
  }
  return result
}

收集索引

skipIndexes 用来标识传入的 value 是否为 arrayargumentsbuffertypedArray 等类数组类型,如果是,则还需要收集索引。

首先获取 value 的长度,如果 skipIndexes 标识为 true,则初始化一个长度为 length 的数组,用来保存索引。

while (++index < length) {
  result[index] = `${index}`
}

如果 skipIndexestrue,索引 index 的初始值为 -1,否则为 length,不会进入这个循环,因为使用 ++index,因此,存入 result 的第一个值为 0

可以看到,后面会用 for...in 收集 key,其实索引也会在 for...in 中遍历出来,那为什么还要多此一举,用 while 循环来遍历索引呢?

因为像数组会有稀疏数组这种情况,在 for...in 中,索引不会全部遍历出来。

收集属性

传入的 value 可能不是以上类型,或者以上的类型还有其他属性,这些属性也是要收集的。

源码如下:

for (const key in value) {
  if ((inherited || hasOwnProperty.call(value, key)) &&
      !(skipIndexes && (
    // Safari 9 has enumerable `arguments.length` in strict mode.
    (key === 'length' ||
     // Skip index properties.
     isIndex(key, length))
  ))) {
    result.push(key)
  }
}

使用 for...in 来遍历,for...in会遍历原型链上的属性,默认只收集 value 本身的属性,即属性要通过 Object.prototype.hasOwnProperty 的检测,但是可以通过传入标志位 inherited 来收集原型链上的属性。

因此收集的属性要满足 (inherited || hasOwnProperty.call(value, key)) 这个条件。

因为类数组的索引已经收集过,因此在 for...in 遍历时需要将索引排除。

条件如下:

!(skipIndexes && isIndex(key, length))

本来 argumentslength 属性是不枚举,但是在 Safari 9 中,在 strict mode 下,length 是可以枚举的,因此也要排除。

最终条件如下:

!(skipIndexes && ((key === 'length' || isIndex(key, length)))

符合条件的数组也存入 result 中。

License

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

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

作者:对角另一面

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
internal 内部函数或方法 系列文章
Projects
None yet
Development

No branches or pull requests

1 participant