0%

【学习Lodash源码】Object.get

Object.get这个方法,我觉得是在工作中比较常用的。而且ES6没有原生实现的。在之前的文章中。尝试用Reduce实现了一个类似的方法。可以在JS数组那篇文章中的末尾找到。我们来看一看Lodash是怎么实现的。

我们先来看看他的用法。这个函数的主要作用是,根据对象的路径获取值。

1
2
3
4
5
6
7
8
9
10
const object = { 'a': [{ 'b': { 'c': 3 } }] }

get(object, 'a[0].b.c')
// => 3

get(object, ['a', '0', 'b', 'c'])
// => 3

get(object, 'a.b.c', 'default')
// => 'default'

我们可以看出来,路径可以是一个字符串,可以是一个数组。我们最后还可以给一个默认值,当根据这路径没有找到对应值得时候,而返回默认值。

下面我们来看一下源码:

1
2
3
4
5
6
function get(object, path, defaultValue) {
const result = object == null ? undefined : baseGet(object, path)
return result === undefined ? defaultValue : result
}

export default

我们可以看到如果传入的对象为undefined或者根据路径查找出的结果为undefined的时候,就返回defaultValue。否则返回查找的结果。主要的逻辑还是在baseGet中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import castPath from './castPath.js'
import toKey from './toKey.js'

function baseGet(object, path) {
path = castPath(path, object)

let index = 0
const length = path.length

while (object != null && index < length) {
object = object[toKey(path[index++])]
}
return (index && index == length) ? object : undefined
}

export default baseGet

在baseGet中引入了两个方法。首先是这个castPath这个方法是强制转换为path数组。可以先看一下它的实现。

1
2
3
4
5
6
function castPath(value, object) {
if (Array.isArray(value)) {
return value
}
return isKey(value, object) ? [value] : stringToPath(value)
}

如果本身就是一个path数组,就返回value。如果不是的话,就调用isKey这个方法isKey这个方法主要是用来检测value是否是属性名,而不是属性路径。我们先不看里面的具体实现。就看最后的return,如果这个value是对象的名的话(也就是传入的这个value不带.或者[])我们就直接返回这个数组只包含value这一个值。否则返回通过stringToPath这个函数,返回的路径数组。

我们回到前面一个函数,我们看下面while部分的内容。

1
2
3
while (object != null && index < length) {
object = object[toKey(path[index++])]
}

如果object不为空,我们就一直取数组里面的值,然后通过数组里面的key找到对应的value。起初刚看这个时候,还在想直接用object=是不是修改传入的对象。后面才发现真的是基础不牢。其实在JavaScript函数的参数都是值传递,而传入对象也只是传入对象地址的值。我们在用object进行赋值的时候。实际是切断了与传入地址之间的关系。所以并不会改变原对象。这部分之后会单独开一篇文章进行讲解。剩下的核心就是toKey这个方法。
这个方法也很简单

1
2
3
4
5
6
7
8
9
const INFINITY = 1 / 0;

function toKey(value) {
if (typeof value === 'string' || isSymbol(value)) {
return value
}
const result = `${value}`
return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result
}

这个方法就是讲不是string和Symbol的key转为string。不得不佩服这种开源库想的真周到。
我们可以看到其中有一个非常冷面的知识点。
就是当你给一个变量赋值为+0或者0的时候,它都为0。而赋值为-0的时候它等于-0;

1
2
3
const a = 0; //console -> 0
const b = +0; //console -> 0
const c = -0; //console -> 0

而经过字符串转换的时候,0、+0、-0都会转换为0。Lodash居然考虑到了这一点,他用 1/value 是否等于 -(1/0)来判断传入的这个value是否为-0。从而修复了这一个问题。

又扯远了。我们回去继续看。其实核心代码很简单,就是一个不断根据key取值的一个过程。但是通过源码的学习。我们能学习到很多冷门的知识点和一些边界检测。这个过程也是十分有意思的。