序文フロントエンドで作業している友人は、バックエンドから返されるデータが複数のレイヤーにネストされている状況に遭遇したことがあるはずです。深いオブジェクトの値を取得したいときは、次のようにレイヤーごとに空でないかどうかのチェックを行ってエラーを防止します。 定数オブジェクト = { 品: 名前: 'a', タグ: { 名前: 'Fast'、 id: 1, タグタイプ: { 名前: 'ラベル' } } } } tagType.nameを取得する必要がある場合、判断は次のようになります (obj.goods !== nullの場合 && obj.goods.tags !== null && obj.goods.tags.tagType !== null) { } 属性名が長いと、コードが判読できなくなります。 もちろん、ECMAScript 2020 ではこの問題を解決するために ?. が導入されました。 name = obj?.goods?.tags?.tageType?.name; とします。 しかし、ES2020 と互換性のないブラウザにはどのように対処すればよいのでしょうか? 文章lodash を使用したことがある学生は、lodash に get メソッドがあることを知っているかもしれません。公式 Web サイトには次のように書かれています。 _.get(オブジェクト, パス, [デフォルト値]) オブジェクトのパスに応じて値を取得します。 解決された値が未定義の場合は、defaultValue に置き換えられます。 パラメータ
例var オブジェクト = { 'a': [{ 'b': { 'c': 3 } }] }; _.get(オブジェクト、'a[0].b.c'); // => 3 _.get(オブジェクト、['a', '0', 'b', 'c']); // => 3 _.get(オブジェクト, 'abc', 'デフォルト'); // => 'デフォルト' これで問題は解決しますが、(残念ながら「しかし」があります) プロジェクトや会社の要件など、さまざまな理由により lodash ライブラリを使用できない場合はどうなりますか? さて、lodash がどのように実装されているかを見てみましょう。コードを抽出するだけではだめでしょうか? そうすれば、再び楽しくレンガを動かすことができます~~ Lodash 実装:関数 get(オブジェクト, パス, デフォルト値) { const 結果 = オブジェクト == null ? 未定義 : baseGet(オブジェクト、パス) 結果を返す === undefined ? defaultValue : 結果 } ここでやっていることはとても簡単です。まず戻り値を見てみましょう。オブジェクトがデフォルト値を返す場合、コアコードはbaseGetにあります。baseGetの実装を見てみましょう。 関数baseGet(オブジェクト、パス) { //入力文字列パスを配列に変換します。 パス = castPath(パス、オブジェクト) インデックスを0にする 定数長さ = パス.長さ // 配列を走査してオブジェクトの各レイヤーを取得します while (object != null && index < length) { object = object[toKey(path[index++])] // toKeyメソッド} 戻り値 (インデックス && インデックス == 長さ) ? オブジェクト: 未定義 } ここでは、castPath(入力パスを配列に変換する)とtoKey(実際のキーを変換する)という2つの関数を使用します。 トーキー機能:/** さまざまな `Number` 定数の参照として使用されます。 */ 定数無限大 = 1 / 0 /** * `value` が文字列またはシンボルでない場合は、文字列キーに変換します。 * * @プライベート * @param {*} value 検査する値。 * @returns {string|symbol} キーを返します。 */ 関数toKey(値) { if (typeof value === 'string' || isSymbol(value)) { 戻り値 } const 結果 = `${値}` 戻り値 (結果 == '0' && (1 / 値) == -INFINITY) ? '-0' : 結果 } ここで行われる主なことは 2 つあります。
isSymbol関数は、Symbol型かどうかを判断するためにもここで使用されています。コードはここには掲載されていません。興味のある学生はlodashソースコードを確認してください。 castPath関数:'./isKey.js' から isKey をインポートします。 './stringToPath.js' から stringToPath をインポートします。 /** * `value` がパス配列でない場合はパス配列にキャストします。 * * @プライベート * @param {*} value 検査する値。 * @param {Object} [object] キーを照会するオブジェクト。 * @returns {Array} キャストプロパティパス配列を返します。 */ 関数castPath(値、オブジェクト) { Array.isArray(値)の場合{ 戻り値 } isKey(値、オブジェクト)を返します? [値] : stringToPath(値) } castPath は主に、入力パスを配列に変換して、後続のトラバーサルで深いオブジェクトを取得できるように準備します。 ここではisKey()とstringToPath()が使用されます。 isKey は、現在の値がオブジェクトのキーであるかどうかを判断する比較的単純な関数です。 stringToPathは主に、入力パスが「abc[0].d」のような文字列である場合に処理します。 stringToPath 関数:'./memoizeCapped.js' から memoizeCapped をインポートします。 const charCodeOfDot = '.'.charCodeAt(0) 定数 reEscapeChar = /\(\)?/g 定数rePropName = 正規表現( // ドットまたは括弧以外のものに一致します。 '[^.[\]]+' + '|' + // または、括弧内のプロパティ名を一致させます。 '\[(?:' + // 文字列以外の式に一致します。 '([^"'][^[]*)' + '|' + // または文字列に一致します (エスケープ文字をサポートします)。 '(["'])((?:(?!\2)[^\\]|\\.)*?)\2' + ')\]'+ '|' + // または、連続するドットまたは空の括弧の間のスペースとして "" と一致します。 '(?=(?:\.|\[\])(?:\.|\[\]|$))' 、 'g') /** * `string` をプロパティ パス配列に変換します。 * * @プライベート * @param {string} string 変換する文字列。 * @returns {Array} プロパティ パス配列を返します。 */ const stringToPath = memoizeCapped((文字列) => { 定数結果 = [] (文字列.charCodeAt(0)=== charCodeOfDot)の場合{ 結果.push('') } string.replace(rePropName, (一致, 式, 引用符, 部分文字列) => { キー = 一致させる (引用)の場合{ キー = subString.replace(reEscapeChar, '$1') } そうでない場合 (式) { キー = 式.trim() } 結果.push(キー) }) 結果を返す }) ここでは主にパス内の.と[]を除外し、実際のキーを解析して配列に追加します。 memoizeCapped 関数:'../memoize.js' から memoize をインポートします。 /** 最大メモ化キャッシュサイズとして使用されます。 */ 定数 MAX_MEMOIZE_SIZE = 500 /** * メモ化された関数の内容をクリアする`memoize`の特殊バージョン * `MAX_MEMOIZE_SIZE` を超えるとキャッシュされます。 * * @プライベート * @param {Function} func 出力をメモ化する関数。 * @returns {Function} 新しくメモ化された関数を返します。 */ 関数 memoizeCapped(func) { const 結果 = memoize(func, (キー) => { const { キャッシュ } = 結果 (キャッシュサイズ === MAX_MEMOIZE_SIZE)の場合{ キャッシュをクリアする() } リターンキー }) 結果を返す } エクスポートデフォルトmemoizeCapped キャッシュされたキーには制限があり、500に達するとキャッシュがクリアされます。 メモ化関数:関数 memoize(func, リゾルバ) { if (typeof func !== 'function' || (resolver != null && typeofresolver !== 'function')) { throw new TypeError('関数が必要です') } const メモ化 = 関数(...引数) { const key = リゾルバ ? リゾルバ.apply(this, args) : args[0] const キャッシュ = メモ化されたキャッシュ キャッシュにキーがある場合 cache.get(キー) を返す } const 結果 = func.apply(this, args) memoized.cache = cache.set(キー、結果) || キャッシュ 結果を返す } memoized.cache = new (memoize.Cache || Map) メモ化されたものを返す } memoize.Cache = マップ 実は、最後の 2 つの関数がよくわかりません。入力パスが 'abc' の場合、それを配列に変換すればよいのではないでしょうか。キャッシュにクロージャを使用するのはなぜですか? これを理解できる人が答えてくれることを願っています。 ソースコードは多くの関数を使用しており、異なるファイルに分かれているため、簡略化しました。 完全なコードは次のとおりです。/** * `object`の`path`にある値を取得します。解決された値が * `undefined` の場合、代わりに `defaultValue` が返されます。 * @例 * 定数オブジェクト = { 'a': [{ 'b': { 'c': 3 } }] } * * get(オブジェクト、'a[0].b.c') * // => 3 * * get(オブジェクト、['a', '0', 'b', 'c']) * // => 3 * * get(オブジェクト、'abc'、'default') * // => 'デフォルト' */ safeGet (オブジェクト、パス、デフォルト値) { 結果を出す オブジェクトが null ではない場合 if (!Array.isArray(path)) { const type = typeof パス if (type === 'number' || type === 'boolean' || path == null || /^\w*$/.test(パス) || !(/.|[(?:[^[]]*|(["'])(?:(?!\1)[^\]|\.)*?\1)]/.test(パス)) || (オブジェクト != null && Object(オブジェクト) 内のパス)) { パス = [パス] } それ以外 { 定数結果 = [] (path.charCodeAt(0) === '.'.charCodeAt(0)の場合){ 結果.push('') } 定数rePropName = 正規表現( // ドットまたは括弧以外の任意の文字に一致します。 '[^.[\]]+|\[(?:([^"'][^[]*)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))' 、 'g') path.replace(rePropName, (一致, 式, 引用符, 部分文字列) => { キー = 一致させる (引用)の場合{ キー = subString.replace(/\(\)?/g, '$1') } else if (式) { キー = 式.trim() } 結果.push(キー) }) パス = 結果 } } インデックスを0にする 定数長さ = パス.長さ const toKey = (値) => { if (typeof value === 'string') { 戻り値 } const 結果 = `${値}` 戻り値 (結果 === '0' && (1 / 値) === -(1 / 0)) ? '-0' : 結果 } while (オブジェクト != null && インデックス < 長さ) { オブジェクト = オブジェクト[toKey(パス[インデックス++])] } 結果 = (インデックス && インデックス === 長さ) ? オブジェクト: 未定義 } 結果を返す === undefined ? defaultValue : 結果 } コードはlodashから借用しました 参考文献:
要約するこれで、Js で Object のディープ レベル オブジェクトを安全に取得する方法に関するこの記事は終了です。Js で Object のディープ レベル オブジェクトを取得することに関するより関連性の高いコンテンツについては、123WORDPRESS.COM の以前の記事を検索するか、次の関連記事を引き続き参照してください。今後も 123WORDPRESS.COM を応援していただければ幸いです。 以下もご興味があるかもしれません:
|
<<: MySQLはmysqldump+binlogを使用して、削除されたデータベースの原理分析を完全に復元します。
>>: nginx を使用して静的リソース サーバーを構築する方法
ここでは、dockerがインストールされたcentosサーバーを紹介し、リモートリンクサービスを開始...
目次1. はじめに2. 初期ビュー(I) Vueの概念を理解する(II) MVVMアーキテクチャ(I...
この記事では、Baidu News Navigation Barの効果を実現するための具体的なJSコ...
MySQLデーモンの起動に失敗したエラーの解決方法数日前、公開されたウェブサイトはこれらのアクティビ...
目次概要コールバックまたは高階関数とは何ですか?コールバック関数はどのように機能しますか?コールバッ...
最近、要素テーブルを使用すると、並べ替えの問題によく遭遇します。単純な並べ替えであれば、要素の公式が...
序文: MYSQL は最も人気のある WEB バックエンド データベースです。最近、NOSQL がま...
目次概要方法1: params経由でパラメータを渡す方法2: クエリを通じてパラメータを渡す方法3:...
Postfix は、Linux システム上で電子メールをルーティングまたは配信するために使用される無...
CentOS 7 では、次のようなコマンドを使用してホスト ポートをコンテナー ポートにマッピングす...
1. ブリッジ: デフォルトでは VMnet0 が使用されます1. 原則:ブリッジは、それぞれ 2...
まず、私の日常生活についてお話しします。MySQLの急速なアップデートにより、MySQLはバージョン...
実行環境、Idea2020バージョン、Tomcat10、実行時にTomcat CatalinaLog...
JS を使用してオブジェクト指向メソッドを実装し、JD.com の 5 つ星レビュー効果を実現します...
移動を実現するためにtranslateパラメータを使用しますtranslateX: X 軸に沿って移...