怎样文雅安全地在深层数据结构中取值

古有赵子龙面对“冲锋之势,有进无退,陷阵之志,有死无生”的局势,能万军丛中取敌将首领。
在我们的Javascript中,每每用对象(Object)来存储一个数据构造。假如这个构造异常复杂,那末想要平安文雅地掏出一个值,也并不是简朴。

这篇文章将会细致论述在一个嵌套较深的场景中,怎样平安的完成读写操纵。先后会尝试多种要领,愿望对读者有所启示。

本文示例自创A.Sharif的最新文章:Safely Accessing Deeply Nested Values In JavaScript,喜好看英文原版的同砚可以直接戳链接。

场景引见

在React开辟中,我们依据数据来衬着视图。常常会涌现相似下面这类状况:

const props = {
    user: {
        posts: [
            { title: 'Foo', comments: [ 'Good one!', 'Interesting...' ] },
            { title: 'Bar', comments: [ 'Ok' ] },
            { title: 'Baz', comments: []}
        ],
        comments: [...]
    }
}

这是一个典范的猎取用户批评信息并加以展现的场景。实在,这还嵌套的不够深,试想一个复兴存在多层:复兴的复兴,复兴的复兴的复兴。。。

权且先看我们的示例吧,此时我们想猎取第一个post的批评信息。用传统的javascript要领应当这么做:

props.user &&
props.user.posts &&
props.user.posts[0] &&
props.user.posts[0].comments

或许经验丰富的javascript开辟者会邃晓运用这么多&&的意义。这是为了在对象中相干取值的历程,须要考证每个key和index的存在性。不然会有报错,这将会是致命性的。而且props这个数据构造必定是动态天生的,存在偶然valid偶然invalid的状况。在测试历程当中,很难复现。

一样的为难场景触目皆是,设想一下,假如我们须要猎取一位用户末了一个批评博客的问题,就须要:

props.user &&
props.user.comments &&
props.user.comments[0] &&
props.user.comments[0].blog.title

这些例子夸大吗?实在不然。我们邃晓了,想要猎取一个数据值,须要一层一层遍历属性的存在性。这无疑是烦琐的。

解决计划

如今邃晓了我们面对的搅扰,接下来我会用几种要领:

  • 纯JavaScript要领;

  • 最具有函数式代表的JavaScript库-Ramda,辅以柯粒化(currying)等头脑和计划解决问题。

JavaScript计划

先直接上代码:

const get = (p, o) => p.reduce((xs, x) => (xs && xs[x]) ? xs[x] : null, o)

console.log(get(['user', 'posts', 0, 'comments'], props)) // [ 'Good one!', 'Interesting...' ]
console.log(get(['user', 'post', 0, 'comments'], props)) // null

注重这里我运用了一个ES5中,比较倾向函数式头脑的reduce要领。关于这个要领,我想很多人实在还并不明白,发起先去举行进修,或许参考我之前的一篇文章。

同时,我尝试猎取:user->posts[0]->comments,
并配以一个反例:user->post[0]->comments;
固然,在反例中,post数组并不存在。

我们来剖析一下代码。

const get = (p, o) =>
    p.reduce((xs, x) =>
        (xs && xs[x]) ? xs[x] : null, o)

我们完成的get要领中,吸收两个参数,第一个p示意猎取值的途径(path);别的一个参数示意目的对象。

一样,为了设想上的越发天真和笼统。我们可以柯粒化我们的要领:

const get = p => o =>
    p.reduce((xs, x) =>
        (xs && xs[x]) ? xs[x] : null, o)

如许的话,就可以这个姿态挪用:

const getUserComments = get(['user', 'posts', 0, 'comments'])
console.log(getUserComments(props))
// [ 'Good one!', 'Interesting...' ]
console.log(getUserComments({user:{posts: []}}))
// null

假如关于get要领中reduce的运用还不清晰,那就再看一个简朴的例子:

['id'].reduce((xs, x) => (xs && xs[x]) ? xs[x] : null, {id: 10})
// 返回10

Ramda计划

假如不本身手动设想上述要领的话,我们可以运用Ramda函数式类库完成:

const getUserComments = R.path(['user', 'posts', 0, 'comments'])

接下来挪用须要这个姿态:


getUserComments(props) // [ 'Good one!', 'Interesting...' ]
getUserComments({}) // null

假如我们想在指定途径下未找到一个值时,不返回null,而是返回自定义的内容呢?我们可以运用pathOr要领,第一个参数用来设置默许输出。

const getUserComments = R.pathOr([], ['user', 'posts', 0, 'comments'])
getUserComments(props) // [ 'Good one!', 'Interesting...' ]
getUserComments({}) // []

总结

这篇文章翻译自A.Sharif的最新文章:Safely Accessing Deeply Nested Values In JavaScript,个中后半部份未做翻译。
后半部份实在剖析了 Ramda+Folktale的完成,以及Ramda+Lenses的完成。

Folktale和Lenses是异常函数式Functional Programming的头脑,明白起来相对艰涩且比较小众。有兴致的读者可以点击原文去自行相识。

Happy Coding!

假如你对函数式编程并不伤风,大可只进修第一部份的完成。关于函数式编程有兴致的同砚,愿望这篇文章可以举一反三,迎接与我交换。

PS: 作者Github堆栈,迎接经由过程代码各种形式交换。

    原文作者:lucas_580e331d326b4
    原文地址: https://segmentfault.com/a/1190000009265903
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞