【JS】41-如何優雅地鏈式取值

做者:@灰風GreyWind前端

https://juejin.im/post/5ba08483e51d450e99430a7fgit


開發中,鏈式取值是很是正常的操做,如:github

res.data.goods.list[0].price

可是對於這種操做報出相似於Uncaught TypeError: Cannot read property 'goods' of undefined 這種錯誤也是再正常不過了,若是說是res數據是本身定義,那麼可控性會大一些,可是若是這些數據來自於不一樣端(如先後端),那麼這種數據對於咱們來講咱們都是不可控的,所以爲了保證程序可以正常運行下去,咱們須要對此校驗:swift

if (res.data.goods.list[0] && res.data.goods.list[0].price) {
// your code
}

若是再精細一點,對於全部都進行校驗的話,就會像這樣:後端

if (res && res.data && res.data.goods && res.data.goods.list && res.data.goods.list[0] && res.data.goods.list[0].price){
// your code
}

不敢想象,若是數據的層級再深一點會怎樣,這種實現實在是很是不優雅,那麼若是優雅地來實現鏈式取值呢?瀏覽器

1、optional chaining

這是一個出於stage 2的ecma新語法,目前已經有了babel的插件 babel-plugin-transform-optional-chaining,這種語法在swift中有,能夠看下官方給的實例微信

a?.b                          // undefined if `a` is null/undefined, `a.b` otherwise.
a
== null ? undefined : a.b
a
?.[x]                        // undefined if `a` is null/undefined, `a[x]` otherwise.
a
== null ? undefined : a[x]
a
?.b()                        // undefined if `a` is null/undefined
a
== null ? undefined : a.b() // throws a TypeError if `a.b` is not a function// otherwise, evaluates to `a.b()`
a
?.()                        // undefined if `a` is null/undefined
a
== null ? undefined : a()  // throws a TypeError if `a` is neither null/undefined, nor a function// invokes the function `a` otherwise

2、經過函數解析字符串

咱們能夠經過函數解析字符串來解決這個問題,這種實現就是lodash的 _.get 方法babel

var object = { a: [{ b: { c: 3 } }] };
var result = _.get(object, 'a[0].b.c', 1);
console
.log(result);
// output: 3

實現起來也很是簡單,只是簡單的字符串解析而已:app

function get (obj, props, def) {
   
if((obj == null) || obj == null || typeof props !== 'string') return def;
   
const temp = props.split('.');
   
const fieldArr = [].concat(temp);
   temp
.forEach((e, i) => {
       
if(/^(w+)[(w+)]$/.test(e)) {
           
const matchs = e.match(/^(w+)[(w+)]$/);
           
const field1 = matchs[1];
           
const field2 = matchs[2];
           
const index = fieldArr.indexOf(e);
           fieldArr
.splice(index, 1, field1, field2);
       
}
   
})
   
return fieldArr.reduce((pre, cur) => {
       
const target = pre[cur] || def;
       
if(target instanceof Array) {
           
return [].concat(target);
       
}
       
if(target instanceof Object) {
           
return Object.assign({}, target)
       
}
       
return target;
   
}, obj)
}
var c = {a: {b : [1,2,3] }}
get(c ,'a.b')     // [1,2,3]
get(c, 'a.b[1]')  // 2
get(c, 'a.d', 12)  // 12

3、使用解構賦值

這個思路是來自github上 You-Dont-Need-Lodash-Underscore 這個倉庫,看到這個的時候真的佩服函數

const c = {a:{b: [1,2,3,4]}}
const { a: result } = c;
// result : {b: [1,2,3,4]}const {a: { c: result = 12 }} = c
// result: 12

固然,這個時候爲了保證不報uncaught Typeerror,咱們仍然須要定義默認值, 就像這樣, 貌似若是不加lint可讀性堪憂

const {a: {c: {d: result2} = {}}} = c

4、使用Proxy

這個是組內同事提到的,一個簡單實現以下:

function pointer(obj, path = []) {
   
return new Proxy({}, {
       
get (target, property) {
           
return pointer(obj, path.concat(property))
       
},
       apply
(target, self, args) {
           
let val = obj;
           
let parent;
           
for(let i = 0; i < path.length; i++) {
               
if(val === null || val === undefined) break;
               parent
= val;
               val
= val[path[i]]    
           
}
           
if(val === null || val === undefined) {
               val
= args[0]
           
}
           
return val;
       
}
   
})
}

咱們能夠這樣使用:

let c = {a: {b: [1, ,2 ,3]}}
pointer
(c).a();   // {b: [1,2,3]}
pointer
(c).a.b(); // [1,2,3]
pointer
(d).a.b.d('default value');  // default value複製代碼

這差很少就是心中所謂的優雅了。

綜上,在實際工做中,使用方法四會是最優雅,可讀性也很是強,但考慮到瀏覽器的話,可能方法二會更加經常使用,固然,若是你所要取的值層級不是太深,你組內的同事要嚴格的lint,方法三也不失爲一種好的選擇。


本文分享自微信公衆號 - 前端自習課(FE-study)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索