欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Js中安全獲取Object深層對象的方法實例

 更新時間:2021年09月02日 10:56:26   作者:爲爲懿笑  
Object是JavaScript基本數(shù)據(jù)類型之一(function也屬于object,是特殊的object),其存儲于堆中,這篇文章主要給大家介紹了關(guān)于Js中安全獲取Object深層對象的相關(guān)資料,需要的朋友可以參考下

前言

做前端的小伙伴一定遇到過后端返回的數(shù)據(jù)有多層嵌套的情況,當我要獲取深層對象的值時為防止出錯,會做層層非空校驗,比如:

const obj = {
    goods: {
        name: 'a',
        tags: {
            name: '快速',
            id: 1,
            tagType: {
                name: '標簽'
            }
        }
    }
}

當我需要獲取tagType.name時判斷是這樣的

if (obj.goods !== null
    && obj.goods.tags !== null
    && obj.goods.tags.tagType !== null) {
  
}

如果屬性名冗長,這斷代碼就沒法看。

當然ECMAScript2020中已經(jīng)推出了?.來解決這個問題:

let name = obj?.goods?.tags?.tageType?.name;

但是不兼容ES2020的瀏覽器中怎么處理呢?

正文

使用過lodash的同學可能知道,lodash中有個get方法,官網(wǎng)是這么說的:

_.get(object, path, [defaultValue])

根據(jù) object對象的path路徑獲取值。 如果解析 value 是 undefined 會以 defaultValue 取代。

參數(shù)

  1. object (Object) : 要檢索的對象。
  2. path (Array|string) : 要獲取屬性的路徑。
  3. [defaultValue] ()* : 如果解析值是 undefined ,這值會被返回。

例子

var object = { 'a': [{ 'b': { 'c': 3 } }] };
​
_.get(object, 'a[0].b.c');
// => 3 
​
_.get(object, ['a', '0', 'b', 'c']);
// => 3
​
_.get(object, 'a.b.c', 'default');
// => 'default'

如此問題解決,但是(就怕有“但是”)

如果因為項目、公司要求等各種原因我不能使用引入lodash庫怎么辦呢?

有了,我們看看lodash是怎么實現(xiàn)的,把代碼摘出來不就可以了嗎,如此又能快樂的搬磚了~~

lodash的實現(xiàn):

function get(object, path, defaultValue) {
  const result = object == null ? undefined : baseGet(object, path)
  return result === undefined ? defaultValue : result
}

這里做的事很簡單,先看返回,如果object即返回默認值,核心代碼在baseGet中,那我們再來看baseGet的實現(xiàn)

function baseGet(object, path) {
  // 將輸入的字符串路徑轉(zhuǎn)換成數(shù)組,
  path = castPath(path, object)
​
  let index = 0
  const length = path.length
  // 遍歷數(shù)組獲取每一層對象
  while (object != null && index < length) {
    object = object[toKey(path[index++])] // toKey方法
  }
  return (index && index == length) ? object : undefined
}
​

這里又用到兩個函數(shù)castPath(將輸入路徑轉(zhuǎn)換為數(shù)組)、toKey(轉(zhuǎn)換真實key)

tokey函數(shù):

/** Used as references for various `Number` constants. */
const INFINITY = 1 / 0
​
/**
 * Converts `value` to a string key if it's not a string or symbol.
 *
 * @private
 * @param {*} value The value to inspect.
 * @returns {string|symbol} Returns the key.
 */
function toKey(value) {
  if (typeof value === 'string' || isSymbol(value)) {
    return value
  }
  const result = `${value}`
  return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result
}

這里主要做了兩件事,

  • 如果key類型為String或者symbol則直接返回
  • 如果key為其他類型,則轉(zhuǎn)換為String返回

這里還用到了isSymbol函數(shù)來判斷是否為Symbol類型,代碼就不貼了,感興趣的同學可以查看lodash源碼

castPath函數(shù):

import isKey from './isKey.js'
import stringToPath from './stringToPath.js'
​
/**
 * Casts `value` to a path array if it's not one.
 *
 * @private
 * @param {*} value The value to inspect.
 * @param {Object} [object] The object to query keys on.
 * @returns {Array} Returns the cast property path array.
 */
function castPath(value, object) {
  if (Array.isArray(value)) {
    return value
  }
  return isKey(value, object) ? [value] : stringToPath(value)
}
​

castPath主要是將輸入的路徑轉(zhuǎn)換為數(shù)組的,為后面遍歷獲取深層對象做準備。

這里有用到了isKey()與stringToPath().

isKey比較簡單判斷當前value是否為object的key。

stringToPath主要處理輸入路徑為字符串的情況,比如:'a.b.c[0].d'

stringToPath函數(shù):

import memoizeCapped from './memoizeCapped.js'
​
const charCodeOfDot = '.'.charCodeAt(0)
const reEscapeChar = /\(\)?/g
const rePropName = RegExp(
  // Match anything that isn't a dot or bracket.
  '[^.[\]]+' + '|' +
  // Or match property names within brackets.
  '\[(?:' +
    // Match a non-string expression.
    '([^"'][^[]*)' + '|' +
    // Or match strings (supports escaping characters).
    '(["'])((?:(?!\2)[^\\]|\\.)*?)\2' +
  ')\]'+ '|' +
  // Or match "" as the space between consecutive dots or empty brackets.
  '(?=(?:\.|\[\])(?:\.|\[\]|$))'
  , 'g')
​
/**
 * Converts `string` to a property path array.
 *
 * @private
 * @param {string} string The string to convert.
 * @returns {Array} Returns the property path array.
 */
const stringToPath = memoizeCapped((string) => {
  const result = []
  if (string.charCodeAt(0) === charCodeOfDot) {
    result.push('')
  }
  string.replace(rePropName, (match, expression, quote, subString) => {
    let key = match
    if (quote) {
      key = subString.replace(reEscapeChar, '$1')
    }
    else if (expression) {
      key = expression.trim()
    }
    result.push(key)
  })
  return result
})
​

這里主要是排除路徑中的 . 與[],解析出真實的key加入到數(shù)組中

memoizeCapped函數(shù):

import memoize from '../memoize.js'
​
/** Used as the maximum memoize cache size. */
const MAX_MEMOIZE_SIZE = 500
​
/**
 * A specialized version of `memoize` which clears the memoized function's
 * cache when it exceeds `MAX_MEMOIZE_SIZE`.
 *
 * @private
 * @param {Function} func The function to have its output memoized.
 * @returns {Function} Returns the new memoized function.
 */
function memoizeCapped(func) {
  const result = memoize(func, (key) => {
    const { cache } = result
    if (cache.size === MAX_MEMOIZE_SIZE) {
      cache.clear()
    }
    return key
  })
​
  return result
}
​
export default memoizeCapped
​

這里是對緩存的key做一個限制,達到500時清空緩存

memoize函數(shù):

function memoize(func, resolver) {
  if (typeof func !== 'function' || (resolver != null && typeof resolver !== 'function')) {
    throw new TypeError('Expected a function')
  }
  const memoized = function(...args) {
    const key = resolver ? resolver.apply(this, args) : args[0]
    const cache = memoized.cache
​
    if (cache.has(key)) {
      return cache.get(key)
    }
    const result = func.apply(this, args)
    memoized.cache = cache.set(key, result) || cache
    return result
  }
  memoized.cache = new (memoize.Cache || Map)
  return memoized
}
​
memoize.Cache = Map

其實最后兩個函數(shù)沒太看懂,如果輸入的路徑是'a.b.c',那直接將它轉(zhuǎn)換成數(shù)組不就可以嗎?為什么要用到閉包進行緩存。

希望看懂的大佬能給解答一下

由于源碼用到的函數(shù)較多,且在不同文件,我將他們進行了精簡,

完整代碼如下:

/**
   * Gets the value at `path` of `object`. If the resolved value is
   * `undefined`, the `defaultValue` is returned in its place.
   * @example
   * const object = { 'a': [{ 'b': { 'c': 3 } }] }
   *
   * get(object, 'a[0].b.c')
   * // => 3
   *
   * get(object, ['a', '0', 'b', 'c'])
   * // => 3
   *
   * get(object, 'a.b.c', 'default')
   * // => 'default'
   */
  safeGet (object, path, defaultValue) {
    let result
    if (object != null) {
      if (!Array.isArray(path)) {
        const type = typeof path
        if (type === 'number' || type === 'boolean' || path == null ||
        /^\w*$/.test(path) || !(/.|[(?:[^[]]*|(["'])(?:(?!\1)[^\]|\.)*?\1)]/.test(path)) ||
        (object != null && path in Object(object))) {
          path = [path]
        } else {
          const result = []
          if (path.charCodeAt(0) === '.'.charCodeAt(0)) {
            result.push('')
          }
          const rePropName = RegExp(
            // Match anything that isn't a dot or bracket.
            '[^.[\]]+|\[(?:([^"'][^[]*)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))'
            , 'g')
          path.replace(rePropName, (match, expression, quote, subString) => {
            let key = match
            if (quote) {
              key = subString.replace(/\(\)?/g, '$1')
            } else if (expression) {
              key = expression.trim()
            }
            result.push(key)
          })
          path = result
        }
      }
      let index = 0
      const length = path.length
      const toKey = (value) => {
        if (typeof value === 'string') {
          return value
        }
        const result = `${value}`
        return (result === '0' && (1 / value) === -(1 / 0)) ? '-0' : result
      }
      while (object != null && index < length) {
        object = object[toKey(path[index++])]
      }
      result = (index && index === length) ? object : undefined
    }
    return result === undefined ? defaultValue : result
  }

代碼借鑒自lodash

參考資料:

總結(jié)

到此這篇關(guān)于Js中安全獲取Object深層對象的文章就介紹到這了,更多相關(guān)Js獲取Object深層對象內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 簡單談?wù)刯s的數(shù)據(jù)類型

    簡單談?wù)刯s的數(shù)據(jù)類型

    下面小編就為大家?guī)硪黄唵握務(wù)刯s的數(shù)據(jù)類型。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-09-09
  • JavaScript中的高級函數(shù)

    JavaScript中的高級函數(shù)

    在JavaScript中,函數(shù)的功能十分強大,除了函數(shù)相關(guān)的基礎(chǔ)知識外,掌握一些高級函數(shù)并應(yīng)用起來,不僅能讓JS代碼看起來更為精簡,還可以提升性能,本文是小編總結(jié)的一些常用的、重要的函數(shù)
    2018-01-01
  • JS原生瀑布流效果實現(xiàn)

    JS原生瀑布流效果實現(xiàn)

    這篇文章主要介紹了JS原生瀑布流效果實現(xiàn) ,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-04-04
  • JavaScript實現(xiàn)隨機替換圖片的方法

    JavaScript實現(xiàn)隨機替換圖片的方法

    這篇文章主要介紹了JavaScript實現(xiàn)隨機替換圖片的方法,涉及javascript中Math.random方法的靈活運用,需要的朋友可以參考下
    2015-04-04
  • NestJs 靜態(tài)目錄配置詳解

    NestJs 靜態(tài)目錄配置詳解

    這篇文章主要介紹了NestJs 靜態(tài)目錄配置,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-03-03
  • Java/JS獲取flash高寬的具體方法

    Java/JS獲取flash高寬的具體方法

    本文為大家詳細介紹下使用Java/JS如何獲取flash的高寬,下面有個不錯的示例,感興趣的朋友可以參考下,希望對大家有所幫助
    2013-12-12
  • 微信小程序wxml列表渲染原理解析

    微信小程序wxml列表渲染原理解析

    這篇文章主要介紹了微信小程序wxml列表渲染原理解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2019-11-11
  • JavaScript學習小結(jié)(一)——JavaScript入門基礎(chǔ)

    JavaScript學習小結(jié)(一)——JavaScript入門基礎(chǔ)

    本教程比較適合javascript初學者,對javascript基本知識的小結(jié)包括變量,基本類型等知識點,需要的朋友一起來學習吧
    2015-09-09
  • javascript控制frame,iframe的src屬性代碼

    javascript控制frame,iframe的src屬性代碼

    原理就是通過獲取當前網(wǎng)頁地址欄的信息傳參,然后設(shè)置框架的地址。
    2009-12-12
  • JS幻燈片可循環(huán)播放可平滑旋轉(zhuǎn)帶滾動導航(自寫)

    JS幻燈片可循環(huán)播放可平滑旋轉(zhuǎn)帶滾動導航(自寫)

    本文為大家介紹下實現(xiàn)JS幻燈片可循環(huán)播放帶滾動導航可平滑旋轉(zhuǎn)的全過程,效果還不錯,由需要的朋友可以參考下,希望對大家有所幫助
    2013-08-08

最新評論