Lodash实战练习之手写get函数

Lodash的介绍

Lodash 是一个一致性、模块化、高性能的 JavaScript 实用工具库,Lodash 通过降低 array、number、objects、string 等等的使用难度从而让 JavaScript 变得更简单。

简单来说,Lodash 是一套工具库,内部封装了很多字符串、数组、对象等常见数据类型的处理函数,大大提高工作效率。

你可能会说 Lodash 里面的好多方法都能自己实现,没必要再引入额外的工具库来增加项目的体积,但是并不能保证实现得比 Lodash 好。我们自己在项目中封装的工具函数,很多时候功能并不是完善的,尤其是一些边界值的处理,而且需要考虑到各种使用场景,Lodash 这些方面就做得很好。

而且 Lodash 支持多种模块化方案,配合 tree-shaking 技术或者使用单独的函数模块,几乎不会导致冗余代码。

js 复制代码
// 方式1:引入整个lodash对象
import _ from "lodash";
// 方式2:按名称引入特定的函数
import { cloneDeep } from "lodash";
 
// 上述2种方式都会引入整个lodash库,体积大,而下面2种方式都能实现按需引入,减小体积
// 1.只引入cloneDeep函数
import cloneDeep from "lodash/cloneDeep";
// 2.使用lodash-es
import { cloneDeep } from "lodash-es";

基于上面的这些优点,Lodash 已经变得非常流行,目前被很多项目应用,越来越受欢迎,Github 上目前已经有 59.1k 的 Star

既然 Lodash 这么优秀,那么我们很应该去学习一下它内部函数的实现,从而提升自己的编码水平,因此这篇文章与大家一同手写实现 get 函数,手写 get 函数也是前端一道高频的面试题。

get函数的使用

首先我们得知道 get 函数是如何使用的?有哪些参数?每个参数的具体作用?

通过文档可以知道,Lodash.get 的使用:_.get(object, path, [defaultValue]) ,接收三个参数

  1. object (Object) : 要检索的对象
  2. path (Array|string) : 要获取属性的路径
  3. [defaultValue] : 如果解析值是 undefined ,这值会被返回

来看一个例子:

js 复制代码
var 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'

通过这个例子 get 函数的作用已经很明显了:根据 path 去依次取对象 object 中的成员的值,比如 _.get(object, 'a[0].b.c'),首先取 object 中的 a 属性,然后取 0 属性,接着取 b 属性,最后取 c 属性,如果对象中没有对应要取的属性,返回 undefined,这时若指定第三个参数 default,那么当前面取值解析为 undefined 时会返回 default。

手写实现简易版

下面开始手写实现一个简易版的 get 函数

1. 基本功能的实现

先考虑 path 为数组的情况,并且先不管第三个参数;path 为数组类型比较好处理,直接遍历数组拿到所有属性即可:

js 复制代码
function _get(object, path) {
  let obj = object;
  for (const key of path) {
    obj = obj[key];
  }
  return obj;
};

测试代码:

js 复制代码
const object = { a: [{ b: { c: 3 } }] };
console.log('-------', _get(object, ['a', '0', 'b', 'c']));
console.log('-------', _get(object, ['a', '0']));
console.log('-------', _get(object, ['a', '0', 'b']));
console.log('-------', _get(object, ['a']));

结果如下,已经基本符合 get 函数的功能:

2. 路径类型的处理

接着考虑 path 为字符串的情况,我这里的思路是,只需加一个类型转化的逻辑,将字符串转为对应的数组即可,然后其他的逻辑与上面保持一致。

利用正则表达式,匹配字符串中的非[ ] . 字符,使用 match 方法得到一个数组,举个例子:a[0].b.c => ['a', '0', 'b', 'c']

Lodash 源码里对字符串的匹配更加复杂,需要考虑到各种字符,我这里的正则表达式比较简单,怎么简单怎么来~

js 复制代码
function _get(object, path) {
  let obj = object;
  if (typeof path === 'string') {
    const reg = /[^\[\].]+/g;
    path = path.match(reg);
  }
  for (const key of path) {
    obj = obj[key];
  }
  return obj;
}

测试代码:

js 复制代码
const object = { a: [{ b: { c: 3 } }] };
console.log('-------', _get(object, 'a[0].b.c'));
console.log('-------', _get(object, 'a[0]'));
console.log('-------', _get(object, 'a[0].b'));
console.log('-------', _get(object, 'a'));

结果如下,与上面 path 为数组的结果是一样的,说明代码没问题:

3. 处理访问不到的属性

现在的 get 方法还存在一个问题,那就是如果我访问对象 object 中不存在的属性:

js 复制代码
console.log(_get(object, ['w', '0', 'b', 'c']));

是会报错的,但是 Lodash 的 get 函数不会报错,而是会返回 undefined,所以需要做些处理:

结果打印 undefined,不会报错。

4. 第三个参数

最后考虑下第三个参数,就是当我前面取值解析为 undefined 时会返回 default,这里很简单,在刚刚的判断语句里返回默认值就好了:

这里还有个小问题,就是如果最后一个属性取不到,目前会返回 undefined

js 复制代码
console.log(_get(object, ['a', '0', 'b', 'w'], '我是默认值'));  // undefined

但是正常情况下应该返回第三个参数才对的,因此需要对 get 函数的返回值做处理:

5. 最终版代码

js 复制代码
function _get(object, path, defaultValue) {
  let obj = object;
  if (typeof path === 'string') {
    const reg = /[^\[\].]+/g;
    path = path.match(reg);
  }
  for (const key of path) {
    if (!obj) {
      return defaultValue;
    }
    obj = obj[key];
  }
  return obj === undefined ? defaultValue : obj;
}

最终版的代码如上,代码不多,看起来也很好理解,关键在于一些细节以及边界值的处理,希望能帮到对大家有所帮助!

写在最后

这就是一个 get 函数简易版本的实现了,Lodash 的源码里其实比这还要复杂,我们实现成上面的样子已经足够了,至少原理方面已经理解透彻,面试时手写也没啥问题,已经达到了手写 get 函数的目的!

如果有讲得不好的地方,大家可以批评指正,一起交流进步🥳🥳

期待大家的点赞 + 关注,多多支持,你的支持是我前进的动力!🤗🤗

相关推荐
半桔16 分钟前
【Linux手册】从接口到管理:Linux文件系统的核心操作指南
android·java·linux·开发语言·面试·系统架构
开开心心就好27 分钟前
电脑息屏工具,一键黑屏超方便
开发语言·javascript·电脑·scala·erlang·perl
江号软件分享28 分钟前
有效保障隐私,如何安全地擦除电脑上的敏感数据
前端
web守墓人1 小时前
【前端】ikun-markdown: 纯js实现markdown到富文本html的转换库
前端·javascript·html
Savior`L2 小时前
CSS知识复习5
前端·css
许白掰2 小时前
Linux入门篇学习——Linux 工具之 make 工具和 makefile 文件
linux·运维·服务器·前端·学习·编辑器
中微子6 小时前
🔥 React Context 面试必考!从源码到实战的完整攻略 | 99%的人都不知道的性能陷阱
前端·react.js
秋田君7 小时前
深入理解JavaScript设计模式之命令模式
javascript·设计模式·命令模式
中微子7 小时前
React 状态管理 源码深度解析
前端·react.js
风吹落叶花飘荡8 小时前
2025 Next.js项目提前编译并在服务器
服务器·开发语言·javascript