如何優(yōu)雅安全地在深層數(shù)據(jù)結(jié)構(gòu)中取值

image.png

在我們的 Javascript 中葱她,往往用對象(Object)來存儲一個數(shù)據(jù)結(jié)構(gòu)涵亏。如果這個結(jié)構(gòu)非常復(fù)雜晰韵,那么想要安全優(yōu)雅地取出一個值链瓦,也并非簡單拆魏。

這篇文章將會詳細闡述在一個嵌套較深的場景中,如何安全的完成讀寫操作慈俯。先后會嘗試多種方法渤刃,希望對讀者有所啟發(fā)。

場景介紹

在 React 開發(fā)中贴膘,我們根據(jù)數(shù)據(jù)來渲染視圖卖子。經(jīng)常會出現(xiàn)類似下面這種情況:

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

這是一個典型的獲取用戶評論信息并加以展示的場景。其實刑峡,這還嵌套的不夠深洋闽,試想一個回復(fù)存在多層:回復(fù)的回復(fù),回復(fù)的回復(fù)的回復(fù)突梦。诫舅。。

姑且先看我們的示例吧阳似,此時我們想獲取第一個 post 的評論信息骚勘。用傳統(tǒng)的 javascript 方法應(yīng)該這么做:

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

也許經(jīng)驗豐富的javascript開發(fā)者會明白使用這么多 && 的意義。這是為了在對象中相關(guān)取值的過程,需要驗證每一個key和index的存在性俏讹。否則會有報錯当宴,這將是致命性的。并且 props 這個數(shù)據(jù)結(jié)構(gòu)必然是動態(tài)生成的泽疆,存在有時 valid 有時 invalid 的情況户矢。在測試過程中,很難復(fù)現(xiàn)殉疼。

同樣的尷尬場景比比皆是梯浪,想象一下,如果我們需要獲取一名用戶最后一個評論博客的題目瓢娜,就需要:

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

這些例子夸張嗎挂洛?其實不然。我們明白了眠砾,想要獲取一個數(shù)據(jù)值虏劲,需要一層一層遍歷屬性的存在性。這無疑是繁瑣的褒颈。

解決方案

現(xiàn)在明白了我們面臨的困擾柒巫,接下來我會用幾種方法:

  • 純JavaScript方法;
  • 最具有函數(shù)式代表的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 中刨疼,比較偏向函數(shù)式思想的 reduce 方法泉唁。關(guān)于這個方法,我想很多人其實還并不理解币狠,建議先去進行學(xué)習(xí)游两,或者參考我之前的文章:面試題目別有洞天 -> 從es6優(yōu)雅解法,到降級polyfill漩绵,再到redux reducer迷之命名贱案。

  • 同時,我嘗試獲戎雇隆:user->posts[0]->comments宝踪;
  • 并配以一個反例:user->post[0]->comments;當(dāng)然碍扔,在反例中瘩燥,post數(shù)組并不存在。

我們來分析一下代碼不同。

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

我們實現(xiàn)的get方法中厉膀,接收兩個參數(shù):

  • 第一個p表示獲取值的路徑(path)溶耘;
  • 另外一個參數(shù)表示目標(biāo)對象。

同樣服鹅,為了設(shè)計上的更加靈活和抽象凳兵。我們可以柯粒化我們的方法:

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

這樣的話企软,就可以這個姿勢調(diào)用:

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

如果關(guān)于get方法中reduce的使用還不清楚庐扫,那就再看一個簡單的例子:

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

方案二:Ramda方案

如果不自己手動設(shè)計上述方法的話,我們可以使用 Ramda 函數(shù)式類庫完成:

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

接下來調(diào)用需要這個姿勢:

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

如果我們想在指定路徑下未找到一個值時仗哨,不返回 null形庭,而是返回自定義的內(nèi)容呢?

我們可以使用 pathOr 方法厌漂,第一個參數(shù)用來設(shè)置默認輸出萨醒。

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

總結(jié)

其實再進一步,還可以使用 Ramda+Folktale 的實現(xiàn)苇倡,以及 Ramda+Lenses 實現(xiàn)验靡。

Folktale 和 Lenses 代表了非常函數(shù)式 Functional Programming 的思想,理解起來相對晦澀且比較小眾雏节。有興趣的讀者可以自行了解,這里不再展開高职。

如果你對函數(shù)式編程并不感冒钩乍,大可只學(xué)習(xí)第一部分的實現(xiàn)。對于函數(shù)式編程有興趣的同學(xué)怔锌,希望這篇文章能夠拋磚引玉寥粹,歡迎與我交流。

本文示例借鑒A.Sharif的最新文章:Safely Accessing Deeply Nested Values In JavaScript埃元,喜歡看英文原版的同學(xué)可以直接戳鏈接涝涤。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市岛杀,隨后出現(xiàn)的幾起案子阔拳,更是在濱河造成了極大的恐慌,老刑警劉巖类嗤,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件糊肠,死亡現(xiàn)場離奇詭異,居然都是意外死亡遗锣,警方通過查閱死者的電腦和手機货裹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來精偿,“玉大人弧圆,你說我怎么就攤上這事赋兵。” “怎么了搔预?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵霹期,是天一觀的道長。 經(jīng)常有香客問我斯撮,道長经伙,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任勿锅,我火速辦了婚禮帕膜,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘溢十。我一直安慰自己垮刹,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布张弛。 她就那樣靜靜地躺著荒典,像睡著了一般。 火紅的嫁衣襯著肌膚如雪吞鸭。 梳的紋絲不亂的頭發(fā)上寺董,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天,我揣著相機與錄音刻剥,去河邊找鬼遮咖。 笑死,一個胖子當(dāng)著我的面吹牛造虏,可吹牛的內(nèi)容都是我干的御吞。 我是一名探鬼主播,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼漓藕,長吁一口氣:“原來是場噩夢啊……” “哼陶珠!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起享钞,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤揍诽,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后嫩与,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體寝姿,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年划滋,在試婚紗的時候發(fā)現(xiàn)自己被綠了饵筑。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡处坪,死狀恐怖根资,靈堂內(nèi)的尸體忽然破棺而出架专,到底是詐尸還是另有隱情,我是刑警寧澤玄帕,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布部脚,位于F島的核電站,受9級特大地震影響裤纹,放射性物質(zhì)發(fā)生泄漏委刘。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一鹰椒、第九天 我趴在偏房一處隱蔽的房頂上張望锡移。 院中可真熱鬧,春花似錦漆际、人聲如沸淆珊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽施符。三九已至,卻和暖如春擂找,著一層夾襖步出監(jiān)牢的瞬間戳吝,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工贯涎, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留骨坑,地道東北人。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓柬采,卻偏偏與公主長得像,于是被迫代替她去往敵國和親且警。 傳聞我的和親對象是個殘疾皇子粉捻,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,877評論 2 345

推薦閱讀更多精彩內(nèi)容