値のフェッチ時に、未定義のプロパティ 'xx' が読み取れないのを回避する。
この記事は 私のブログ 私のブログはGitHubでご覧いただけます。
値を取得するとき、特に連鎖した値を取得するとき、しばしば
Cannot read property 'xx' of undefined
これを避けるにはどうしたらよいのでしょうか。回避する方法をいくつか紹介します。
実績のあるライブラリのメソッドを使用する
これは最もシンプルな方法のひとつです。
_.get
メソッドを使うか、Ramda を導入して
R.path
メソッドを使用すれば、上記のエラーのリスクを回避することができます。
この方法はとても効果的で便利なのですが、他の方法にも目を通してほしいのです
&&と||を使用する。
JavaScriptでは、&&や||演算子を使うと、次の例のように、最終的に返される値が必ずしもboolean型にならないことが分かっています。
console.log(undefined && "a"); //undefined
console.log("a" && "b"); //b
console.log(undefined || "a"); //a
console.log("a" || "b"); //a
&&: 最初の項がfalsy(ダミー値、ブール文脈では「偽」に変換されると判断された値)である場合は最初の項を返し、そうでない場合は2番目の項を返します。
||: 第一の項が偽の場合、第二の項を返し、そうでない場合は第一の項を返します。
このルールを使って、値を取得することができます。
まず、データをフィッティングしてみましょう
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の分解代入を利用して、プロパティにデフォルト値を与え、エラーを回避することができるのです。例として、上記のアートクルデータは以下のようになります。
const { authorInfo: { author } = {} } = artcle;
console.log(author); //Bowen
上記のようにすると、多くの変数をいきなり公開することになるので、次のように関数をラップするだけでよいのです。
const getAuthor = ({ authorInfo: { author } = {} } = {}) => author;
console.log(getAuthor(artcle)); //Bowen
これは変数を公開しないので、getAuthor関数を再利用することができ、よりエレガントです。
try catchの使用
取り込み中にエラーが発生することもあるので、当然ながら
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
この方法の悪い点は、1 つの 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でプロキシを活用した非常に興味深い書き込みを紹介します。
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
原理は比較的単純で、ポインタメソッドがプロキシオブジェクトとして null 関数を持つ Proxy インスタンスを返し、値が取得されるたびにキーが保存されて
proxyArtcle.authorInfo.author
例えば、次のように等価です。
pointer(artcle, ["authorInfo", "author"])
. applyはパス配列を順番に繰り返し、値を取り続けられないと判断するとループから抜け出します。
プロキシについてまだ学んでいない方は、数分かけて理解してください。 プロキシ
これはよりエレガントなソリューションのように思えますが、プロキシはブラウザとノードのバージョンによって制限され、ポリフィルは不可能なので、実際のアプリケーションには考えることが多すぎます。
オプションのチェーニング
これはまだ提案段階の新機能で、以下の通りです。 tc39/proposal-optional-chaining が、すでに使用できるバベルがあります。 babel-plugin-proposal-optional-chaining
この機能は以下のように使うことができます。
console.log(artcle?.authorInfo?.author); //Bowen
console.log(artcle?.timeInfo?.publishTime) //undefined
この方法は完璧に近く、実際に実装されることが期待できます
関連
-
[解決済み] 解決済み】clearInterval()が動作しない [重複] [重複]
-
[解決済み] HTML が読み取れない %0A
-
[解決済み] 空白を除去するための正規表現
-
[解決済み] window.location()が動作せず、ページを開けない
-
[解決済み] ホバー時のJqueryが機能しない
-
[解決済み] NodeJS - nodemonが私のサーバーを再起動しない
-
[解決済み] クエリとは何ですか。url.parse()でnodeJSを使用して、[Object: null prototype]を指定しています。
-
[解決済み] AngularJsのstring.Replace
-
[解決済み] エラーメッセージ「.innerHTMLは関数ではありません」[重複]。
-
js error null のプロパティ 'getAttribute' を読み取れません。
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】文字列から英数字でない文字を削除する
-
[解決済み] Chromeのファイル入力で「選択されたファイルがありません」というツールチップを削除するにはどうすればよいですか?
-
[解決済み] iframeを再読み込み/更新する最良の方法は何ですか?
-
[解決済み] moment.jsで日付をISO 8601のようにフォーマットするにはどうしたらいいですか?
-
[解決済み] JSON の位置 4 に予期しないトークン < があります。
-
[解決済み] WebStormのエラー:式文が代入または呼び出しでない
-
[解決済み] 配列中の最長文字列を検索する
-
[解決済み] ファイアストア 複数の条件付き where 節
-
[解決済み] JavaScriptでキャンバスのサイズを変更するには?
-
"TypeError "です。未定義のプロパティ 'undefined' を読み取れませんでした"