本文來自個人博客,歡迎你們去GitHub上star個人博客node
咱們在取值特別是鏈式取值的時候,經常會遇到Cannot read property 'xx' of undefined
的錯誤,如何避免這種狀況的發生呢?這裏有幾種方法以供參考git
這是最簡單的一種手段:只用引入lodash,使用_.get
方法;或者引入Ramda,使用R.path
方法,咱們就能規避出現上述錯誤的風險es6
儘管這種方法十分奏效且方便,但我仍是但願你能看完其餘方法github
咱們知道,在JavaScript中,使用&&或者||操做符,最後返回的值不必定是boolean類型的,好比下面這個例子:數組
console.log(undefined && "a"); //undefined console.log("a" && "b"); //b console.log(undefined || "a"); //a console.log("a" || "b"); //a
&&:若是第一項是falsy(虛值,Boolean上下文中已認定可轉換爲‘假‘的值),則返回第一項,不然返回第二項瀏覽器
||:若是第一項是falsy,返回第二項,不然返回第一項安全
咱們能夠利用這種規則進行取值babel
咱們先擬一個數據app
const artcle = { authorInfo: { author: "Bowen" }, artcleInfo: { title: "title", timeInfo: { publishTime: "today" } } };
接下來利用&&和||進行安全取值:函數
console.log(artcle.authorInfo && artcle.authorInfo.author); //Bowen console.log(artcle.timeInfo && artcle.timeInfo.publishTime); //undefined console.log(artcle.artcleInfo && artcle.artcleInfo.timeInfo && artcle.artcleInfo.timeInfo.publishTime ); //today console.log((artcle.authorInfo || {}).author); //Bowen console.log((artcle.timeInfo || {}).publishTime); //undefined console.log(((artcle.artcleInfo || {}).timeInfo || {}).publishTime); //today
不難看出,這兩種方法都不算優雅,只適用短鏈式取值,一旦嵌套過深,使用&&須要寫一長段代碼,而使用||須要嵌套不少括號
咱們能夠利用es6的解構賦值,給屬性一個默認值,避免出現錯誤。以上文的artcle數據爲例,以下:
const { authorInfo: { author } = {} } = artcle; console.log(author); //Bowen
上面這麼作會暴露不少變量出來,咱們能夠簡單地封裝一個函數,以下:
const getAuthor = ({ authorInfo: { author } = {} } = {}) => author; console.log(getAuthor(artcle)); //Bowen
這樣作不會將變量暴露出來,同時getAuthor函數也能複用,優雅多了
既然在取值的過程當中會出現錯誤,那咱們天然能夠利用try catch
提早將錯誤捕獲:
let author, publishTime; try { author = artcle.authorInfo.author; } catch (error) { author = null; } try { publishTime = artcle.timeInfo.publishTime; } catch (error) { publishTime = null; } console.log(author); //Bowen console.log(publishTime); //null
這個方法很差的地方在於:咱們沒法在一個try catch語句裏進行屢次取值,由於只要有任一錯誤,就會進入catch語句中去
咱們能夠寫一個通用函數優化這一流程:
const getValue = (fn, defaultVaule) => { try { return fn(); } catch (error) { return defaultVaule; } }; const author = getValue(() => artcle.authorInfo.author); const publishTime = getValue(() => artcle.timeInfo.publishTime); console.log(author); //Bowen console.log(publishTime); //undefined
這是我在網上看到的一個十分有意思的寫法,利用了es6中的proxy完成的:
const pointer = function(obj, path = []) { return new Proxy(function() {}, { get: function(target, key) { return pointer(obj, path.concat(key)); }, apply: function(target, object, args) { let value = obj; for (let i = 0; i < path.length; i++) { if (value == null) { break; } value = value[path[i]]; } if (value === undefined) { value = args[0]; } return value; } }); }; const proxyArtcle = pointer(artcle); console.log(proxyArtcle.authorInfo.author()); //Bowen console.log(proxyArtcle.publishTime()); //undefined
原理比較簡單,咱們能夠看到,pointer方法返回的是一個以空函數爲代理對象的Proxy實例,而在每次取值的時候會將key保存下來,以proxyArtcle.authorInfo.author
爲例,它其實等價於pointer(artcle, ["authorInfo", "author"])
。因爲是以空函數爲代理對象,咱們能夠將執行它,觸發apply。apply中會遍歷path數組依次取值,若是發現沒法繼續取值則break,跳出循環。
若是你尚未學習proxy,能夠花幾分鐘瞭解一下:proxy
這種方法在我看來已經是比較優雅的解決方法,但因爲proxy對瀏覽器和node版本有所限制,且不可能有polyfill,真正應用起來須要考慮太多
這是一個新特性,尚在提案階段,具體能夠看tc39/proposal-optional-chaining,不過已經有babel可使用了:babel-plugin-proposal-optional-chaining
咱們能夠像下面同樣使用這個特性:
console.log(artcle?.authorInfo?.author); //Bowen console.log(artcle?.timeInfo?.publishTime) //undefined
這種方法已接近完美,咱們能夠期待它的真正落實