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 函数的目的!

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

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

相关推荐
三思而后行,慎承诺几秒前
详解React Fiber架构中,reconcile阶段的具体工作流程
javascript·react.js·ecmascript
前端太佬1 分钟前
微信公众号网页登录:前端视角下的技术实现精要
前端·javascript·微信
Ryan今天学习了吗2 分钟前
如何在浏览器中渲染100万个元素,并且保证页面不卡顿?超详细底层原理图文分享
前端
前端太佬3 分钟前
微信小程序支付全流程实战指南(Node.js后端篇)
前端·javascript·微信小程序
_十六3 分钟前
面试官最爱问的 TypeScript 装饰器:核心原理与实战技巧全解析.md
前端·typescript
代码搬运媛3 分钟前
mitt 事件发布-订阅库在 react 中的使用
前端
小桥风满袖4 分钟前
Three.js-硬要自学系列17 (拉伸、扫描、多边形轮廓简介、轮廓圆弧、多边形内孔)
前端·css·three.js
Ryan今天学习了吗5 分钟前
从零开始实现命令式组件ElMessage(附代码)
前端
用户2031196600965 分钟前
padding和frame在使用中的顺序问题
前端