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源码分析之stringToPath #69

Open
HeftyKoo opened this issue Feb 7, 2020 · 0 comments
Open

lodash源码分析之stringToPath #69

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

Comments

@HeftyKoo
Copy link
Owner

HeftyKoo commented Feb 7, 2020

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

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

依赖

import memoizeCapped from './memoizeCapped.js'

《lodash源码分析之memoizeCapped》

源码分析

stringToPath 同样是 lodash 的内部方法,作用是将深层嵌套属性字符串转换成路径数组,即将类似于 a.b.c 这样的字符串,转换成 ['a', 'b', 'c'] 这样的数组,方便 lodash 从数组中将属性一个一个取出,然后取值。

代码如下:

const charCodeOfDot = '.'.charCodeAt(0)
const reEscapeChar = /\\(\\)?/g
const rePropName = RegExp(
  // Match anything that isn't a dot or bracket.
  '[^.[\\]]+' + '|' +
  // Or match property names within brackets.
  '\\[(?:' +
    // Match a non-string expression.
    '([^"\'][^[]*)' + '|' +
    // Or match strings (supports escaping characters).
    '(["\'])((?:(?!\\2)[^\\\\]|\\\\.)*?)\\2' +
  ')\\]'+ '|' +
  // Or match "" as the space between consecutive dots or empty brackets.
  '(?=(?:\\.|\\[\\])(?:\\.|\\[\\]|$))'
  , 'g')
const stringToPath = memoizeCapped((string) => {
  const result = []
  if (string.charCodeAt(0) === charCodeOfDot) {
    result.push('')
  }
  string.replace(rePropName, (match, expression, quote, subString) => {
    let key = match
    if (quote) {
      key = subString.replace(reEscapeChar, '$1')
    }
    else if (expression) {
      key = expression.trim()
    }
    result.push(key)
  })
  return result
})

处理以 . 开头的字符串

const charCodeOfDot = '.'.charCodeAt(0)
const result = []
if (string.charCodeAt(0) === charCodeOfDot) {
  result.push('')
}

. 开头的字符串,lodash 会认定最外层的属性为空字符串。

注意这里用 charCodeAt(0) 来获取 .charCode,再和 string0 位的 charCode 作对比,这里最开始的时候是用正则 /^\./ 来做匹配的,后来有个提交了个 pr 才最终使用 charCode 来比较,因为这样的性能更好。

处理常规路径

lodash 的这个正则相当复杂,主要处理三种情况。

首先看看 a.b.c 这种情况。在这种情况下,可以简化成下面的代码:

string.replace(rePropName, (match) => {
  let key = match
  result.push(key)
})

这种情况下,是最简单的,只有 match 有值,回调函数会调用三次,match 的值分别为 abc。直接将结果pushresult 数组即可。

处理一般中括号取值

在数组取值的情况下,会写上中括号加上索引,如 a[0].b 这样的形式,转换成路径数组是 ['a', '0', 'b']

在这种情况下,可以简化成这样的形式:

string.replace(rePropName, (match, expression) => {
  let key = match
  else if (expression) {
    key = expression.trim()
  }
  result.push(key)
})

在匹配到索引 0 时,match 的结果是 [0],但是这里 expression 的结果是 0 ,因此 key0

处理带引号的情况

中括号的情况下,有些人会习惯性地带上引号,如写成 a['0'].b 的形式,如果这个 string 是通过接口返回的,可以还会带上转义字符,如 a[\'0\'].b,在这种情况下,quote 会有值 , ,这时 subString 为在没转义的情况下为 0,在有转义符的情况下为 \'0\' ,因此需要用 reEscapeChar 正则将转义字符替换掉。

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