古代,赵子龙面临“冲锋陷阵,进而不退,志在必战,有死无生”的局面,他能够从数千名士兵中夺取敌人的头颅。在我们的Javascript中,对象(Object)经常用来存储一个数据结构。如果结构非常复杂,安全优雅地检索值并不容易。本文将详细阐述如何在深度嵌套场景下安全完成读写操作。我会陆续尝试各种方法,希望对读者有所启发。本文示例借鉴了A.Sharif的最新文章:SafelyAccessingDeeplyNestedValuesInJavaScript,喜欢阅读英文原版的同学可以直接点击链接。场景介绍在React开发中,我们根据数据渲染视图。经常会出现这样的情况:constprops={user:{posts:[{title:'Foo',comments:['Goodone!','Interesting...']},{title:'Bar',comments:['Ok']},{title:'Baz',comments:[]}],comments:[...]}}这是典型的获取用户评论信息并展示的场景。其实嵌套不够深。想象一个多层的回复:回复的回复,回复的回复。..让我们先看看我们的例子。此时我们要获取第一条帖子的评论信息。传统的javascript方法应该这样做:props.user&&props.user.posts&&props.user.posts[0]&&props.user.posts[0].comments也许有经验的javascript开发人员会理解使用这么多&&的意义。这是为了获取对象中相关值的过程,需要验证每个键和索引是否存在。否则会报错,这是致命的。而且props的数据结构必须是动态生成的,时而有效时而无效。在测试期间,很难重现。同样尴尬的场景比比皆是。想象一下,如果我们需要获取用户对博客的最后一条评论的标题,我们需要:props.user&&props.user.comments&&props.user.comments[0]&&props.user.comments[0].blog.title是这些例子夸大了吗?其实并不是。我们理解为得到一个数据值,需要逐层遍历属性的存在。这无疑是很麻烦的。解决方案既然我们了解了我们面临的麻烦,我将使用几种方法:纯JavaScript方法;最具代表性的函数式JavaScript库——Ramda,辅以柯里化等思路和方案解决问题。JavaScript方案先直接上传代码:constget=(p,o)=>p.reduce((xs,x)=>(xs&&xs[x])?xs[x]:null,o)console。log(get(['user','posts',0,'comments'],props))//['好一个!','有趣...']console.log(get(['user','post',0,'comments'],props))//null注意这里我使用了ES5中的reduce方法,比较偏向于函数式思维。关于这个方法,我想很多人还是不理解。建议先研究一下,或者参考我之前的一篇文章。同时尝试获取:user->posts[0]->comments,反例:user->post[0]->comments;当然,在反例中,post数组是不存在的。让我们分析一下代码。constget=(p,o)=>p.reduce((xs,x)=>(xs&&xs[x])?xs[x]:null,o)在我们实现的get方法中,我们接收到两个参数,第一个p代表获取值的路径(path);另一个参数表示目标对象。同样,为了更灵活和抽象的设计。我们可以细化我们的方法:constget=p=>o=>p.reduce((xs,x)=>(xs&&xs[x])?xs[x]:null,o)你可以调用这个手势:constgetUserComments=get(['user','posts',0,'comments'])console.log(getUserComments(props))//['好!','有趣...']console.log(getUserComments({user:{posts:[]}}))//null如果get方法中reduce的使用不清楚,再看一个简单的例子:['id']。reduce((xs,x)=>(xs&&xs[x])?xs[x]:null,{id:10})//返回10个Ramda解决方案如果我们不手动设计上面的方法,我们可以使用Ramda函数类型库完成:constgetUserComments=R.path(['user','posts',0,'comments'])接下来的调用需要这样的姿势:getUserComments(props)//['好样的!','Interesting...']getUserComments({})//null如果在指定路径下找不到值时我们想返回自定义内容而不是null怎么办?我们可以使用pathOr方法,第一个参数用来设置默认输出。constgetUserComments=R.pathOr([],['user','posts',0,'comments'])getUserComments(props)//['好!','有趣...']getUserComments({})//[]总结本文翻译自A.Sharif的最新文章:SafelyAccessingDeeplyNestedValuesInJavaScript,后半部分未翻译。下半部分实际分析了Ramda+Folktale和Ramda+Lenses的实现。Folktale和Lenses是非常实用的编程思想,相对晦涩难懂,难以理解。有兴趣的读者可以点击原文自行学习。快乐编码!如果对函数式编程不感兴趣,只能学习第一部分的实现。对于对函数式编程感兴趣的同学,希望这篇文章能对大家有所启发,欢迎与我交流。PS:作者的Github仓库,欢迎通过代码以各种形式交流。
