摘要:如果這個結構非常復雜,那么想要安全優雅地取出一個值,也并非簡單。這是為了在對象中相關取值的過程,需要驗證每一個和的存在性。并且這個數據結構必然是動態生成的,存在有時有時的情況。在測試過程中,很難復現。
古有趙子龍面對“沖鋒之勢,有進無退,陷陣之志,有死無生”的局面,能萬軍叢中取敵將首級。
在我們的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}) // 返回10Ramda方案
如果不自己手動設計上述方法的話,我們可以使用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倉庫,歡迎通過代碼各種形式交流。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/50738.html
摘要:如果這個結構非常復雜,那么想要安全優雅地取出一個值,也并非簡單。這是為了在對象中相關取值的過程,需要驗證每一個和的存在性。并且這個數據結構必然是動態生成的,存在有時有時的情況。在測試過程中,很難復現。 古有趙子龍面對沖鋒之勢,有進無退,陷陣之志,有死無生的局面,能萬軍叢中取敵將首級。在我們的Javascript中,往往用對象(Object)來存儲一個數據結構。如果這個結構非常復雜,那么...
摘要:同行這么做使用實現圓形進度條前端掘金在開發微信小程序的時候,遇到圓形進度條的需求。實現也談數組去重前端掘金的數組去重是一個老生常談的話題了。百度前端技術學院自定義前端掘金一標簽概念元素表示用戶界面中項目的標題。 閑話圖片上傳 - 掘金作者:孫輝,美團金融前端團隊成員。15年畢業加入美團,相信技術,更相信技術只是大千世界里知識的一種,個人博客: https://sunyuhui.com ...
摘要:同行這么做使用實現圓形進度條前端掘金在開發微信小程序的時候,遇到圓形進度條的需求。實現也談數組去重前端掘金的數組去重是一個老生常談的話題了。百度前端技術學院自定義前端掘金一標簽概念元素表示用戶界面中項目的標題。 閑話圖片上傳 - 掘金作者:孫輝,美團金融前端團隊成員。15年畢業加入美團,相信技術,更相信技術只是大千世界里知識的一種,個人博客: https://sunyuhui.com ...
摘要:王國維在人間詞話里談到了治學經驗,他說古今之成大事業大學問者,必經過三種之境界。其中談到中凍結一個對象幾種由淺入深的實踐。王國維已先自表明,吾人可以無勞糾葛。總結本文先后介紹了關于凍結一個對象的三種進階方法。 王國維在《人間詞話》里談到了治學經驗,他說:古今之成大事業、大學問者,必經過三種之境界。 巧合的是,最近受 git chat / git book 邀請,做了一個分享。其中談到J...
閱讀 1483·2023-04-25 15:40
閱讀 2834·2021-08-11 11:15
閱讀 2273·2019-08-26 13:48
閱讀 2844·2019-08-26 12:18
閱讀 2448·2019-08-23 18:23
閱讀 2905·2019-08-23 17:01
閱讀 2978·2019-08-23 16:29
閱讀 1101·2019-08-23 15:15