lodash已死?radash库方法介绍及源码解析 —— 对象方法篇-LMLPHP

写在前面

  • 主页有更多其他篇章的方法,欢迎访问查看。
  • 本篇我们介绍radash中对象相关方法的使用和源码解析。

assign:递归合并两个对象

  1. 使用说明
    • 功能说明:类似于 JavaScript 的 Object.assign 方法,用于将 override 对象的属性和值复制到 initial 对象中。如果属性值是对象,则递归地进行赋值。
    • 参数:初始对象、覆盖对象。
    • 返回值:返回合并后的新对象
  2. 使用代码示例
    import { assign } from 'radash'
    
    const ra = {
      name: 'Ra',
      power: 100
    }
    
    assign(ra, { name: 'Loki' })
    // => { name: Loki, power: 100 }
    
  3. 源码解析
    // 定义一个泛型函数 `assign`。
    export const assign = <X extends Record<string | symbol | number, any>>(
      // `initial` 是初始对象,它的属性可能被 `override` 对象中的属性覆盖。
      initial: X,
      // `override` 是覆盖对象,其属性将覆盖或添加到 `initial` 对象中。
      override: X
    ): X => {
      // 如果 `initial` 或 `override` 为空,则返回非空的那个,或者如果都为空则返回一个空对象。
      if (!initial || !override) return initial ?? override ?? {}
    
      // 使用 `Object.entries` 和 `reduce` 方法合并 `initial` 和 `override` 对象。
      return Object.entries({ ...initial, ...override }).reduce(
        (acc, [key, value]) => {
          // 在每次迭代中,构建累加器 `acc`,它是最终返回的新对象。
          return {
            ...acc,
            [key]: (() => {
              // 如果 `initial` 中的对应属性是一个对象,则递归地调用 `assign` 进行合并。
              if (isObject(initial[key])) return assign(initial[key], value)
              // 如果属性值是数组,这里有一个注释掉的代码行,似乎是未完成的逻辑。
              // if (isArray(value)) return value.map(x => assign)
              // 对于非对象属性,直接使用 `override` 中的值。
              return value
            })()
          }
        },
        {} as X // 初始累加器是一个类型为 `X` 的空对象。
      )
    }
    
    • 方法流程说明:
      1. assign 函数接受两个参数,initialoverride,它们都是对象。
      2. 如果其中一个对象为空,函数将返回另一个非空对象。如果两个对象都为空,函数返回一个空对象。
      3. 如果两个对象都非空,函数使用 Object.entriesreduce 方法遍历 override 对象的所有属性。
      4. 对于每个属性,如果 initial 对象中对应的属性也是一个对象,则递归地调用 assign 函数来合并这两个对象。
      5. 如果 initial 对象中对应的属性不是对象,或 override 对象中的属性在 initial 对象中不存在,则直接使用 override 对象中的值。
      6. 返回合并后的新对象。

clone:浅拷贝对象

  1. 使用说明
    • 功能说明:这个函数接受一个对象 obj 作为参数,并返回这个对象的一个新的浅拷贝。
    • 参数:需要克隆的对象。
    • 返回值:过滤完后的映射值组成的数组。
  2. 使用代码示例
    import { clone } from 'radash'
    
    const ra = {
      name: 'Ra',
      power: 100
    }
    
    const gods = [ra]
    
    clone(ra) // => copy of ra
    clone(gods) // => copy of gods
    
  3. 源码解析
    // 定义一个泛型函数 `clone`。
    export const clone = <T>(obj: T): T => {
      // 如果 `obj` 是原始值(如数字、字符串或布尔值),则不需要克隆,直接返回 `obj`。
      if (isPrimitive(obj)) {
        return obj
      }
    
      // 如果 `obj` 是一个函数,则通过 `bind` 方法创建一个新的绑定函数,
      // 并将其绑定到一个空对象上,以创建一个函数的副本。
      if (typeof obj === 'function') {
        return obj.bind({})
      }
    
      // 获取 `obj` 的构造函数,并使用 `new` 操作符创建一个新的对象实例。
      // 这个方法同样适用于创建数组的副本。
      const newObj = new ((obj as object).constructor as { new (): T })()
    
      // 遍历 `obj` 的所有自有属性,并将它们复制到新对象 `newObj` 中。
      Object.getOwnPropertyNames(obj).forEach(prop => {
        // 这里使用了类型断言 `(newObj as any)` 和 `(obj as any)` 来绕过类型检查,
        // 因为函数开头已经检查了原始值的情况。
        ;(newObj as any)[prop] = (obj as any)[prop]
      })
    
      // 返回新创建的对象 `newObj`。
      return newObj
    }
    
    • 方法流程说明:
      1. 如果 obj 是一个原始值,直接返回它,因为原始值在JavaScript中是不可变的。
      2. 如果 obj 是一个函数,使用 bind 方法创建并返回一个新的函数副本。
      3. 如果 obj 是一个对象或数组,使用 obj 的构造函数创建一个新的空对象或空数组 newObj
      4. 遍历 obj 的自有属性,并将每个属性值复制到 newObj 中。
      5. 返回新对象 newObj

construct:把扁平对象构建为深层对象(多维)

  1. 使用说明
    • 功能说明:这个函数接受一个对象 obj 作为参数,并返回这个对象的一个新的浅拷贝。
    • 参数:键值对对象。
    • 返回值:构建后的新对象。
  2. 使用代码示例
    import { construct } from 'radash'
    
    const flat = {
      name: 'ra',
      power: 100,
      'friend.name': 'loki',
      'friend.power': 80,
      'enemies.0.name': 'hathor',
      'enemies.0.power': 12
    }
    
    construct(flat)
    // {
    //   name: 'ra',
    //   power: 100,
    //   friend: {
    //     name: 'loki',
    //     power: 80
    //   },
    //   enemies: [
    //     {
    //       name: 'hathor',
    //       power: 12
    //     }
    //   ]
    // }
    
  3. 源码解析
    // 定义一个泛型函数 `construct`。
    export const construct = <TObject extends object>(obj: TObject): object => {
      // 如果 `obj` 为空,则直接返回一个新的空对象。
      if (!obj) return {}
    
      // 使用 `Object.keys` 获取 `obj` 的所有自有属性的键名,
      // 然后使用 `reduce` 方法来构建新对象。
      return Object.keys(obj).reduce((acc, path) => {
        // 对每个属性键名 `path`,调用 `set` 函数来设置新对象 `acc` 的属性。
        // `(obj as any)[path]` 获取 `obj` 中对应属性的值。
        // `set` 函数的具体实现未提供,我们可以假设它正确地设置了 `acc` 的属性。
        return set(acc, path, (obj as any)[path])
      }, {}) // 初始累加器 `acc` 是一个空对象。
    }
    
    • 方法流程说明:
      1. construct 函数接受一个对象 obj 作为参数。
      2. 如果 obj 为空,函数返回一个空对象。
      3. 如果 obj 非空,函数遍历 obj 的所有自有属性。
      4. 对于每个属性,函数使用 set 函数将 obj 中的属性值复制到新对象中。
      5. 返回构建完成的新对象。

crush:把深层(多维)对象构建成扁平对象(construct的相反操作)

  1. 使用说明
    • 功能说明:这个函数接受一个对象 obj 作为参数,并返回这个对象的一个新的浅拷贝。
    • 参数:对象。
    • 返回值:构建后的扁平数组。
  2. 使用代码示例
    import { crush } from 'radash'
    
    const ra = {
      name: 'ra',
      power: 100,
      friend: {
        name: 'loki',
        power: 80
      },
      enemies: [
        {
          name: 'hathor',
          power: 12
        }
      ]
    }
    
    crush(ra)
    // {
    //   name: 'ra',
    //   power: 100,
    //   'friend.name': 'loki',
    //   'friend.power': 80,
    //   'enemies.0.name': 'hathor',
    //   'enemies.0.power': 12
    // }
    
  3. 源码解析
    // 定义一个泛型函数 `crush`。
    export const crush = <TValue extends object>(value: TValue): object => {
      // 如果 `value` 为空,则直接返回一个空对象。
      if (!value) return {}
    
      // 使用 `objectify` 函数来构建新对象。
      // `objectify` 函数的具体实现未提供,我们可以假设它以某种方式构建对象。
      return objectify(
        // 使用 `keys` 函数获取 `value` 的所有键名。
        // `keys` 函数的具体实现未提供,我们可以假设它返回对象的所有键名。
        keys(value),
        // 使用标识函数 `k => k` 作为键生成函数,意味着新对象的键将与原始对象的键相同。
        k => k,
        // 使用 `get` 函数获取 `value` 对象中每个键对应的值。
        // `get` 函数的具体实现未提供,我们可以假设它正确地获取了对象中的属性值。
        k => get(value, k)
      )
    }
    
    • 方法流程说明:
      1. crush 函数接受一个对象 value 作为参数。
      2. 如果 value 为空,函数返回一个空对象。
      3. 如果 value 非空,函数使用 keys 函数获取 value 的所有键名。
      4. 函数调用 objectify,传入键名数组、键生成函数(这里是标识函数 k => k)和值生成函数(通过调用 get(value, k) 获取每个键的值)。
      5. objectify 函数返回一个新对象,这个对象的属性和值与 value 相同。

get:获取对象中的任意属性,可以通过 . 的方式获取深层的属性

  1. 使用说明
    • 功能说明:用于安全地访问嵌套对象的属性,即使某些中间属性不存在也不会抛出错误。如果找不到指定的路径,则返回一个默认值。
    • 参数:目标对象、属性字符(可以.表示深层次属性)、默认值。
    • 返回值:找到则返回指定值,否则返回默认值。
  2. 使用代码示例
    import { get } from 'radash'
    
    const fish = {
      name: 'Bass',
      weight: 8,
      sizes: [
        {
          maturity: 'adult',
          range: [7, 18],
          unit: 'inches'
        }
      ]
    }
    
    get( fish, 'sizes[0].range[1]' ) // 18
    get( fish, 'sizes.0.range.1' ) // 18
    get( fish, 'foo', 'default' ) // 'default'
    
  3. 源码解析
    // 定义一个泛型函数 `get`。
    export const get = <TDefault = unknown>(
      // 第一个参数 `value` 是一个任意类型的值,它是要访问属性的对象。
      value: any,
      // 第二个参数 `path` 是一个字符串,表示对象属性的路径,使用点或方括号表示法。
      path: string,
      // 第三个可选参数 `defaultValue` 是一个默认值,如果无法访问路径,则返回此值。
      defaultValue?: TDefault
    ): TDefault => {
      // 使用正则表达式分割路径字符串,得到一个属性名的数组 `segments`。
      const segments = path.split(/[.[]]/g)
      // 初始化一个变量 `current`,用于遍历属性路径。
      let current: any = value
      // 遍历 `segments` 中的每个属性名 `key`。
      for (const key of segments) {
        // 如果 `current` 是 `null` 或 `undefined`,则返回 `defaultValue`。
        if (current === null || current === undefined) return defaultValue as TDefault
        // 移除属性名中的引号。
        const dequoted = key.replace(/['"]/g, '')
        // 如果属性名为空(可能是由于路径字符串中的多余点或方括号),则跳过此迭代。
        if (dequoted.trim() === '') continue
        // 将 `current` 更新为当前属性名 `dequoted` 对应的值。
        current = current[dequoted]
      }
      // 如果最终的 `current` 是 `undefined`,则返回 `defaultValue`。
      if (current === undefined) return defaultValue as TDefault
      // 否则,返回找到的值。
      return current
    }
    
    • 方法流程说明:
      1. get 函数接受一个对象 value,一个表示属性路径的字符串 path,以及一个可选的默认值 defaultValue
      2. 使用正则表达式分割 path 字符串,得到一个表示属性链的数组 segments
      3. 遍历 segments 数组,依次访问每个属性。
      4. 如果在访问过程中遇到 nullundefined,或者到达路径的末尾但找不到值,则返回 defaultValue
      5. 如果成功访问到路径上的所有属性,并找到了值,则返回这个值。

invert:把对象中的keyvalue对调

  1. 使用说明
    • 功能说明:用于反转对象的键和值。这意味着原始对象的键成为新对象的值,原始对象的值成为新对象的键。
    • 参数:需要反转的对象。
    • 返回值:反转后的新数组。
  2. 使用代码示例
    import { invert } from 'radash'
    
    const powersByGod = {
      ra: 'sun',
      loki: 'tricks',
      zeus: 'lighning'
    }
    
    invert(gods) // => { sun: ra, tricks: loki, lightning: zeus }
    
  3. 源码解析
    // 定义一个泛型函数 `invert`。
    export const invert = <
      // 泛型 `TKey` 表示对象的键的类型。
      TKey extends string | number | symbol,
      // 泛型 `TValue` 表示对象的值的类型。
      TValue extends string | number | symbol
    >(
      // 参数 `obj` 是一个对象,其键的类型为 `TKey`,值的类型为 `TValue`。
      obj: Record<TKey, TValue>
    ): Record<TValue, TKey> => {
      // 如果 `obj` 为空,则返回一个空对象。
      if (!obj) return {} as Record<TValue, TKey>
      // 使用 `Object.keys` 获取 `obj` 的所有键,并断言键的类型为 `TKey`。
      const keys = Object.keys(obj) as TKey[]
      // 使用 `reduce` 方法遍历所有键,创建一个新对象,其键为 `obj` 的值,值为 `obj` 的键。
      return keys.reduce((acc, key) => {
        // 将 `obj` 的值 `obj[key]` 作为新对象 `acc` 的键,原始键 `key` 作为新对象的值。
        acc[obj[key]] = key
        // 返回累加器 `acc`,它是正在构建的反转对象。
        return acc
      }, {} as Record<TValue, TKey>) // 初始累加器是一个空对象,类型为 `Record<TValue, TKey>`。
    }
    
    • 方法流程说明:
      1. invert 函数接受一个对象 obj 作为参数。
      2. 如果 obj 为空,函数返回一个空对象。
      3. 如果 obj 非空,函数遍历 obj 的所有键。
      4. 对于每个键,函数将 obj 中的值作为新对象的键,将原始键作为新对象的值。
      5. 返回构建完成的新对象,它是 obj 的反转版本。

keys:获取对象中所有的key,包括深层的(表示成a.b的形式)

  1. 使用说明
    • 功能说明:于获取一个对象的所有嵌套属性键的完整路径。这些路径用点号连接,表示从根到叶子的完整路径。
    • 参数:目标对象。
    • 返回值:过滤完后的映射值组成的数组。
  2. 使用代码示例
    import { keys } from 'radash'
    
    const ra = {
      name: 'ra',
      power: 100,
      friend: {
        name: 'loki',
        power: 80
      },
      enemies: [
        {
          name: 'hathor',
          power: 12
        }
      ]
    }
    
    keys(ra)
    // => [
    //   'name',
    //   'power',
    //   'friend.name',
    //   'friend.power',
    //   'enemies.0.name',
    //   'enemies.0.power'
    // ]
    
  3. 源码解析
    // 定义一个泛型函数 `keys`。
    export const keys = <TValue extends object>(value: TValue): string[] => {
      // 如果 `value` 为空,则返回一个空数组。
      if (!value) return []
    
      // 定义一个递归函数 `getKeys`,用于遍历嵌套对象或数组并获取所有键的路径。
      const getKeys = (nested: any, paths: string[]): string[] => {
        // 如果当前值 `nested` 是一个对象,递归地遍历它的每一个键值对。
        if (isObject(nested)) {
          return Object.entries(nested).flatMap(([k, v]) =>
            // 对于每一个键值对,将键 `k` 添加到路径数组 `paths` 中,
            // 并继续递归地遍历值 `v`。
            getKeys(v, [...paths, k])
          )
        }
        // 如果当前值 `nested` 是一个数组,递归地遍历它的每一个元素。
        if (isArray(nested)) {
          return nested.flatMap((item, i) => 
            // 对于每一个元素,将索引 `i` 转换为字符串并添加到路径数组 `paths` 中,
            // 然后继续递归地遍历元素 `item`。
            getKeys(item, [...paths, `${i}`])
          )
        }
        // 如果当前值 `nested` 既不是对象也不是数组,说明已经到达叶子节点,
        // 将路径数组 `paths` 连接成字符串并返回。
        return [paths.join('.')]
      }
      // 使用 `getKeys` 函数从根开始遍历 `value`,并获取所有键的路径。
      return getKeys(value, [])
    }
    
    • 方法流程说明:
      1. keys 函数接受一个对象 value 作为参数。
      2. 如果 value 为空,函数返回一个空数组。
      3. 如果 value 非空,函数定义了一个递归辅助函数 getKeys
      4. getKeys 函数递归地遍历嵌套对象或数组,收集所有键的路径。
      5. 对于嵌套对象,getKeys 遍历每个键值对,并对值递归调用自身。
      6. 对于嵌套数组,getKeys 遍历每个元素,并对元素递归调用自身,使用元素索引作为路径的一部分。
      7. 当到达叶子节点(即不再是对象或数组)时,getKeys 将收集到的路径数组 paths 连接成字符串并返回。
      8. keys 函数返回一个包含所有属性键路径的数组。

listify:把对象的键值对转换成一个由特定结构元素组成的数组

  1. 使用说明
    • 功能说明:用于将一个对象的键值对转换成一个由特定结构元素组成的数组。这个函数接受两个参数:一个对象 obj 和一个转换函数 toItemtoItem 函数接受对象的每个键和对应的值,并返回一个新的元素。
    • 参数:目标对象、条件函数。
    • 返回值:构建后的结果数组。
  2. 使用代码示例
    import { listify } from 'radash'
    
    const fish = {
      marlin: {
        weight: 105,
      },
      bass: {
        weight: 8,
      }
    }
    
    listify(fish, (key, value) => ({ ...value, name: key })) // => [{ name: 'marlin', weight: 105 }, { name: 'bass', weight: 8 }]
    
  3. 源码解析
    // 定义一个泛型函数 `listify`。
    export const listify = <TValue, TKey extends string | number | symbol, KResult>(
      // 参数 `obj` 是一个对象,其键的类型为 `TKey`,值的类型为 `TValue`。
      obj: Record<TKey, TValue>,
      // 参数 `toItem` 是一个函数,它接受一个键和一个值,并返回一个新的结果类型 `KResult` 的元素。
      toItem: (key: TKey, value: TValue) => KResult
    ) => {
      // 如果 `obj` 为空,则返回一个空数组。
      if (!obj) return []
      // 使用 `Object.entries` 获取 `obj` 的所有键值对。
      const entries = Object.entries(obj)
      // 如果 `obj` 没有键值对,返回一个空数组。
      if (entries.length === 0) return []
      // 使用 `reduce` 方法遍历所有键值对,构建结果数组。
      return entries.reduce((acc, entry) => {
        // 对每个键值对,调用 `toItem` 函数,并将返回的元素添加到累加器数组 `acc` 中。
        acc.push(toItem(entry[0] as TKey, entry[1] as TValue))
        // 返回累加器 `acc`。
        return acc
      }, [] as KResult[]) // 初始累加器是一个空数组,其元素类型为 `KResult`。
    }
    
    • 方法流程说明:
      1. listify 函数接受一个对象 obj 和一个转换函数 toItem 作为参数。
      2. 如果 obj 为空,函数返回一个空数组。
      3. 如果 obj 非空,函数使用 Object.entries 获取 obj 的所有键值对。
      4. 如果 obj 没有键值对,函数返回一个空数组。
      5. 如果 obj 有键值对,函数使用 reduce 方法遍历键值对数组。
      6. 对于每个键值对,函数调用 toItem 函数,传入键和值,并将返回的新元素添加到累加器数组中。
      7. 返回构建完成的结果数组。

lowerize:将对象的所有key转换为小写形式

  1. 使用说明
    • 功能说明:用于将一个对象的所有键转换为小写形式,内部用到mapKeys,而 mapKeys 函数则用于一般性地将一个对象的键通过一个映射函数转换为新的键。
    • 参数:对象、映射函数。
    • 返回值:返回一个键是小写形式的对象。
  2. 使用代码示例
    import { lowerize } from 'radash'
    
    const ra = {
      Mode: 'god',
      Power: 'sun'
    }
    
    lowerize(ra) // => { mode, power }
    
  3. 源码解析
    // 定义一个泛型函数 `lowerize`。
    export const lowerize = <T extends Record<string, any>>(obj: T) =>
      // 调用 `mapKeys` 函数将 `obj` 的所有键转换为小写。
      mapKeys(obj, k => k.toLowerCase()) as LowercasedKeys<T>
      // `LowercasedKeys<T>` 是一个类型,表示将 `T` 的键转换为小写后的新类型。
    
    // 定义一个泛型函数 `mapKeys`。
    export const mapKeys = <
      // `TValue` 表示对象的值的类型。
      TValue,
      // `TKey` 表示对象原始键的类型。
      TKey extends string | number | symbol,
      // `TNewKey` 表示映射后的新键的类型。
      TNewKey extends string | number | symbol
    >(
      // `obj` 是一个对象,其键的类型为 `TKey`,值的类型为 `TValue`。
      obj: Record<TKey, TValue>,
      // `mapFunc` 是一个映射函数,它接受一个键和一个值,并返回一个新的键。
      mapFunc: (key: TKey, value: TValue) => TNewKey
    ): Record<TNewKey, TValue> => {
      // 使用 `Object.keys` 获取 `obj` 的所有键,并断言键的类型为 `TKey`。
      const keys = Object.keys(obj) as TKey[]
      // 使用 `reduce` 方法遍历所有键,创建一个新对象,其键为 `mapFunc` 函数的返回值,值为 `obj` 的值。
      return keys.reduce((acc, key) => {
        // 调用 `mapFunc` 函数获取新键,并将 `obj` 中的值赋给新键。
        acc[mapFunc(key as TKey, obj[key])] = obj[key]
        // 返回累加器 `acc`,它是正在构建的新对象。
        return acc
      }, {} as Record<TNewKey, TValue>) // 初始累加器是一个空对象,类型为 `Record<TNewKey, TValue>`。
    }
    
    • 方法流程说明:
      1. lowerize 函数接受一个对象 obj 作为参数。
      2. 它调用 mapKeys 函数,传入 obj 和一个将键转换为小写的函数。
      3. mapKeys 函数遍历 obj 的所有键,并将每个键转换为小写。
      4. mapKeys 返回一个新对象,这个对象的键是小写形式,值与原始对象 obj 相同。
      5. lowerize 函数返回这个新对象,并使用 LowercasedKeys<T> 类型断言来指明返回值的类型。

upperize:将对象的所有key转换为大写形式

  1. 使用说明
    • 功能说明:用于将一个对象的所有键转换为大写形式,而 mapKeys 函数则用于一般性地将一个对象的键通过一个映射函数 mapFunc 转换为新的键。
    • 参数:目标对象、映射函数。
    • 返回值:转换后的新对象。
  2. 使用代码示例
    import { upperize } from 'radash'
    
    const ra = {
      Mode: 'god',
      Power: 'sun'
    }
    
    upperize(ra) // => { MODE, POWER }
    
  3. 源码解析
    // 定义一个泛型函数 `upperize`。
    export const upperize = <T extends Record<string, any>>(obj: T) =>
      // 调用 `mapKeys` 函数将 `obj` 的所有键转换为大写。
      mapKeys(obj, k => k.toUpperCase()) as UppercasedKeys<T>
      // `UppercasedKeys<T>` 是一个类型,表示将 `T` 的键转换为大写后的新类型。
    
    // 定义一个泛型函数 `mapKeys`。
    export const mapKeys = <
      // `TValue` 表示对象的值的类型。
      TValue,
      // `TKey` 表示原始对象的键的类型。
      TKey extends string | number | symbol,
      // `TNewKey` 表示映射后的新键的类型。
      TNewKey extends string | number | symbol
    >(
      // `obj` 是一个对象,其键的类型为 `TKey`,值的类型为 `TValue`。
      obj: Record<TKey, TValue>,
      // `mapFunc` 是一个映射函数,它接受一个键和一个值,并返回一个新的键。
      mapFunc: (key: TKey, value: TValue) => TNewKey
    ): Record<TNewKey, TValue> => {
      // 使用 `Object.keys` 获取 `obj` 的所有键,并断言它们的类型为 `TKey`。
      const keys = Object.keys(obj) as TKey[]
      // 使用 `reduce` 方法遍历所有键,创建一个新对象,其键为 `mapFunc` 函数的返回值,值为 `obj` 的值。
      return keys.reduce((acc, key) => {
        // 调用 `mapFunc` 函数获取新键,并将 `obj` 中的值赋给新键。
        acc[mapFunc(key as TKey, obj[key])] = obj[key]
        // 返回累加器 `acc`,它是正在构建的新对象。
        return acc
      }, {} as Record<TNewKey, TValue>) // 初始累加器是一个空对象,类型为 `Record<TNewKey, TValue>`。
    }
    
    • 方法流程说明:
      1. upperize 函数接受一个对象 obj 作为参数。
      2. 它调用 mapKeys 函数,传入 obj 和一个将键转换为大写的函数。
      3. mapKeys 函数遍历 obj 的所有键,并将每个键转换为大写。
      4. mapKeys 返回一个新对象,这个对象的键是大写形式,值与原始对象 obj 相同。
      5. upperize 函数返回这个新对象,并使用 UppercasedKeys<T> 类型断言来指明返回值的类型。

mapEntries:将对象的键值对通过一个转换函数映射为新的对象

  1. 使用说明
    • 功能说明:用于将一个对象的键值对通过一个转换函数 toEntry 映射为新的键值对,并创建一个新的对象。这个函数接受两个参数:一个对象 obj 和一个转换函数 toEntrytoEntry 函数接受对象的每个键和对应的值,并返回一个包含新键和新值的元组。
    • 参数:目标对象、转换函数。
    • 返回值:构建完成的结果对象。
  2. 使用代码示例
    import { mapEntries } from 'radash'
    
    const ra = {
      name: 'Ra',
      power: 'sun',
      rank: 100,
      culture: 'egypt'
    }
    
    mapEntries(ra, (key, value) => [key.toUpperCase(), `${value}`]) // => { NAME: 'Ra', POWER: 'sun', RANK: '100', CULTURE: 'egypt' }
    
  3. 源码解析
    // 定义一个泛型函数 `mapEntries`。
    export const mapEntries = <
      // `TKey` 表示原始对象的键的类型。
      TKey extends string | number | symbol,
      // `TValue` 表示原始对象的值的类型。
      TValue,
      // `TNewKey` 表示新对象的键的类型。
      TNewKey extends string | number | symbol,
      // `TNewValue` 表示新对象的值的类型。
      TNewValue
    >(
      // `obj` 是一个对象,其键的类型为 `TKey`,值的类型为 `TValue`。
      obj: Record<TKey, TValue>,
      // `toEntry` 是一个转换函数,它接受一个键和一个值,并返回一个新的键值对的元组。
      toEntry: (key: TKey, value: TValue) => [TNewKey, TNewValue]
    ): Record<TNewKey, TNewValue> => {
      // 如果 `obj` 为空,则返回一个空对象。
      if (!obj) return {} as Record<TNewKey, TNewValue>
    
      // 使用 `Object.entries` 获取 `obj` 的所有键值对,并使用 `reduce` 方法来构建新对象。
      return Object.entries(obj).reduce((acc, [key, value]) => {
        // 对每个键值对,调用 `toEntry` 函数,并获取新键 `newKey` 和新值 `newValue`。
        const [newKey, newValue] = toEntry(key as TKey, value as TValue)
        // 将新键值对添加到累加器对象 `acc` 中。
        acc[newKey] = newValue
        // 返回累加器 `acc`。
        return acc
      }, {} as Record<TNewKey, TNewValue>) // 初始累加器是一个空对象,类型为 `Record<TNewKey, TNewValue>`。
    }
    
    • 方法流程说明:
      1. mapEntries 函数接受一个对象 obj 和一个转换函数 toEntry 作为参数。
      2. 如果 obj 为空,函数返回一个空对象。
      3. 如果 obj 非空,函数使用 Object.entries 获取 obj 的所有键值对。
      4. 函数使用 reduce 方法遍历键值对数组。
      5. 对于每个键值对,函数调用 toEntry 函数,传入键和值,并将返回的新键值对添加到累加器数组中。
      6. 返回构建完成的结果对象。

mapKeys:把对象的kye通过一个映射函数转换为新的key

  1. 使用说明
    • 功能说明:将一个对象的键通过一个映射函数 mapFunc 转换为新的键,并创建一个新对象。这个函数接受两个参数:一个对象 obj 和一个映射函数 mapFuncmapFunc 函数接受对象的每个键和对应的值,并返回一个新的键。
    • 参数:目标对象、映射函数。
    • 返回值:构建完成的新对象。
  2. 使用代码示例
    import { mapKeys } from 'radash'
    
    const ra = {
      mode: 'god',
      power: 'sun'
    }
    
    mapKeys(ra, key => key.toUpperCase()) // => { MODE, POWER }
    mapKeys(ra, (key, value) => value) // => { god: 'god', power: 'power' }
    
  3. 源码解析
    // 定义一个泛型函数 `mapKeys`。
    export const mapKeys = <
      // `TValue` 表示对象的值的类型。
      TValue,
      // `TKey` 表示原始对象的键的类型。
      TKey extends string | number | symbol,
      // `TNewKey` 表示映射后的新键的类型。
      TNewKey extends string | number | symbol
    >(
      // `obj` 是一个对象,其键的类型为 `TKey`,值的类型为 `TValue`。
      obj: Record<TKey, TValue>,
      // `mapFunc` 是一个映射函数,它接受一个键和一个值,并返回一个新的键。
      mapFunc: (key: TKey, value: TValue) => TNewKey
    ): Record<TNewKey, TValue> => {
      // 使用 `Object.keys` 获取 `obj` 的所有键,并断言它们的类型为 `TKey`。
      const keys = Object.keys(obj) as TKey[]
      // 使用 `reduce` 方法遍历所有键,创建一个新对象,其键为 `mapFunc` 函数的返回值,值为 `obj` 的值。
      return keys.reduce((acc, key) => {
        // 调用 `mapFunc` 函数获取新键,并将 `obj` 中的值赋给新键。
        acc[mapFunc(key as TKey, obj[key])] = obj[key]
        // 返回累加器 `acc`,它是正在构建的新对象。
        return acc
      }, {} as Record<TNewKey, TValue>) // 初始累加器是一个空对象,类型为 `Record<TNewKey, TValue>`。
    }
    
    • 方法流程说明:
      1. mapKeys 函数接受一个对象 obj 和一个映射函数 mapFunc 作为参数。
      2. 函数使用 Object.keys 获取 obj 的所有键。
      3. 函数使用 reduce 方法遍历键数组。
      4. 对于每个键,函数调用 mapFunc 函数,传入键和值,并将返回的新键作为新对象的键。
      5. 新对象的值与原始对象 obj 中的值相同。
      6. 返回构建完成的新对象。

mapValues:对象的value通过一个映射函数转换为新的value

  1. 使用说明
    • 功能说明:将一个对象的值通过一个映射函数 mapFunc 转换为新的值,并创建一个新对象。这个函数接受两个参数:一个对象 obj 和一个映射函数 mapFuncmapFunc 函数接受对象的每个值和对应的键,并返回一个新的值。
    • 参数:目标对象、映射函数。
    • 返回值:构建完成的新对象。
  2. 使用代码示例
    import { clone } from 'radash'
    
    const ra = {
      name: 'Ra',
      power: 100
    }
    
    const gods = [ra]
    
    clone(ra) // => copy of ra
    clone(gods) // => copy of gods
    
  3. 源码解析
    // 定义一个泛型函数 `mapValues`。
    export const mapValues = <
      // `TValue` 表示原始对象的值的类型。
      TValue,
      // `TKey` 表示对象键的类型。
      TKey extends string | number | symbol,
      // `TNewValue` 表示映射后的新值的类型。
      TNewValue
    >(
      // `obj` 是一个对象,其键的类型为 `TKey`,值的类型为 `TValue`。
      obj: Record<TKey, TValue>,
      // `mapFunc` 是一个映射函数,它接受一个值和一个键,并返回一个新的值。
      mapFunc: (value: TValue, key: TKey) => TNewValue
    ): Record<TKey, TNewValue> => {
      // 使用 `Object.keys` 获取 `obj` 的所有键,并断言它们的类型为 `TKey`。
      const keys = Object.keys(obj) as TKey[]
      // 使用 `reduce` 方法遍历所有键,创建一个新对象,其键与 `obj` 相同,值为 `mapFunc` 函数的返回值。
      return keys.reduce((acc, key) => {
        // 调用 `mapFunc` 函数获取新值,并将其赋给新对象 `acc` 的同名键。
        acc[key] = mapFunc(obj[key], key)
        // 返回累加器 `acc`,它是正在构建的新对象。
        return acc
      }, {} as Record<TKey, TNewValue>) // 初始累加器是一个空对象,类型为 `Record<TKey, TNewValue>`。
    }
    
    • 方法流程说明:
      1. mapValues 函数接受一个对象 obj 和一个映射函数 mapFunc 作为参数。
      2. 函数使用 Object.keys 获取 obj 的所有键。
      3. 函数使用 reduce 方法遍历键数组。
      4. 对于每个键,函数调用 mapFunc 函数,传入值和键,并将返回的新值作为新对象的值。
      5. 新对象的键与原始对象 obj 中的键相同。
      6. 返回构建完成的新对象。

omit:创建一个省略了 keys 数组中指定的一些键的新对象

  1. 使用说明
    • 功能说明:创建一个新的对象,该对象是原始对象 obj 的副本,但省略了 keys 数组中指定的一些键。这个函数接受两个参数:一个对象 obj 和一个包含要省略键名的数组 keys
    • 参数:目标对象、要省略的key数组。
    • 返回值:省略了指定键的新对象。
  2. 使用代码示例
    import { omit } from 'radash'
    
    const fish = {
      name: 'Bass',
      weight: 8,
      source: 'lake',
      brackish: false
    }
    
    omit(fish, ['name', 'source']) // => { weight, brackish }
    
  3. 源码解析
    // 定义一个泛型函数 `omit`。
    export const omit = <T, TKeys extends keyof T>(
      // `obj` 是一个对象,其类型为泛型 `T`。
      obj: T,
      // `keys` 是一个数组,包含对象 `obj` 中要省略的键名,键名的类型为 `TKeys`。
      keys: TKeys[]
    ): Omit<T, TKeys> => {
      // 如果 `obj` 为空,则返回一个空对象。
      if (!obj) return {} as Omit<T, TKeys>
      // 如果 `keys` 为空或长度为0,则返回原始对象 `obj`。
      if (!keys || keys.length === 0) return obj as Omit<T, TKeys>
      // 使用 `reduce` 方法遍历 `keys` 数组,从 `obj` 中省略指定的键。
      return keys.reduce(
        (acc, key) => {
          // 在这个较为局限的上下文中,允许直接在累加器对象 `acc` 上使用 `delete` 操作符。
          // 这是出于性能考虑,通常不建议在其他地方使用这种模式。
          delete acc[key]
          // 返回更新后的累加器 `acc`。
          return acc
        },
        // 使用对象展开运算符 `{ ...obj }` 创建 `obj` 的浅拷贝,以避免直接修改原始对象。
        { ...obj }
      )
    }
    
    • 方法流程说明:
      1. omit 函数接受一个对象 obj 和一个键名数组 keys 作为参数。
      2. 如果 obj 为空,函数返回一个空对象。
      3. 如果 keys 为空或长度为0,函数返回原始对象 obj
      4. 如果 keys 非空,函数使用 reduce 方法遍历 keys 数组。
      5. 对于每个键名 key,函数使用 delete 操作符将其从累加器对象 acc 中删除。
      6. 函数返回省略了指定键的新对象。

pick:创建一个只包含原始对象中指定的 keys`的对象

  1. 使用说明
    • 功能说明:创建一个新的对象,该对象只包含原始对象 obj 中指定的 keys。这个函数接受两个参数:一个对象 obj 和一个包含要选择键名的数组 keys
    • 参数:目标对象、需要包含的key的数组
    • 返回值:过滤完后的映射值组成的数组。
  2. 使用代码示例
    import { pick } from 'radash'
    
    const fish = {
      name: 'Bass',
      weight: 8,
      source: 'lake',
      barckish: false
    }
    
    pick(fish, ['name', 'source']) // => { name, source }
    
  3. 源码解析
    // 定义一个泛型函数 `pick`。
    export const pick = <T extends object, TKeys extends keyof T>(
      // `obj` 是一个对象,其类型为泛型 `T`。
      obj: T,
      // `keys` 是一个数组,包含对象 `obj` 中要选择的键名,键名的类型为 `TKeys`。
      keys: TKeys[]
    ): Pick<T, TKeys> => {
      // 如果 `obj` 为空,则返回一个空对象。
      if (!obj) return {} as Pick<T, TKeys>
      // 使用 `reduce` 方法遍历 `keys` 数组,从 `obj` 中选择指定的键。
      return keys.reduce((acc, key) => {
        // 检查 `obj` 是否自身拥有属性 `key`(不是从原型链继承来的)。
        if (Object.prototype.hasOwnProperty.call(obj, key)) 
          // 如果 `obj` 拥有 `key`,则将其添加到累加器对象 `acc` 中。
          acc[key] = obj[key]
        // 返回累加器 `acc`。
        return acc
      }, {} as Pick<T, TKeys>) // 初始累加器是一个空对象,类型为 `Pick<T, TKeys>`。
    }
    
    • 方法流程说明:
      1. pick 函数接受一个对象 obj 和一个键名数组 keys 作为参数。
      2. 如果 obj 为空,函数返回一个空对象。
      3. 如果 obj 非空,函数使用 reduce 方法遍历 keys 数组。
      4. 对于每个键名 key,函数检查 obj 是否自身拥有这个属性(而不是从原型链继承的)。
      5. 如果 obj 自身拥有这个属性,函数将这个属性及其值添加到累加器对象 acc 中。
      6. 函数返回包含所选键的新对象。

set:在一个对象中设置一个由点或方括号表示法指定的路径上的值

  1. 使用说明
    • 功能说明:用于在一个对象中设置一个值,该值位于由点或方括号表示法指定的路径上。如果路径中的任何中间对象不存在,set 函数将创建它们。
    • 参数:初始对象、属性路径字符串、设置值。
  2. 使用代码示例
    import { set } from 'radash'
    
    set({}, 'name', 'ra')
    // => { name: 'ra' }
    
    set({}, 'cards[0].value', 2)
    // => { cards: [{ value: 2 }] }
    
  3. 源码解析
    // 定义一个泛型函数 `set`。
    export const set = <T extends object, K>(
      // `initial` 是初始对象,我们将在其中设置值。
      initial: T,
      // `path` 是一个字符串,表示要设置值的属性路径。
      path: string,
      // `value` 是我们要设置在路径上的值。
      value: K
    ): T => {
      // 如果 `initial` 为空,则返回一个空对象。
      if (!initial) return {} as T
      // 如果 `path` 为空或 `value` 未定义,则返回原始对象 `initial`。
      if (!path || value === undefined) return initial
      // 使用正则表达式分割路径字符串,得到一个属性名的数组 `segments`。
      // 过滤掉空字符串,确保所有段都是有效的。
      const segments = path.split(/[.[]]/g).filter(x => !!x.trim())
    
      // 定义一个递归辅助函数 `_set`。
      const _set = (node: any) => {
        // 如果路径 `segments` 有多个段,我们需要深入嵌套结构。
        if (segments.length > 1) {
          // 弹出当前段的键名 `key`。
          const key = segments.shift() as string
          // 检查下一个段是否表示数组索引。
          const nextIsNum = toInt(segments[0], null) === null ? false : true
          // 如果当前键不存在,创建一个新的对象或数组,取决于下一个段是否是数字。
          node[key] = node[key] === undefined ? (nextIsNum ? [] : {}) : node[key]
          // 递归调用 `_set` 函数,继续设置值。
          _set(node[key])
        } else {
          // 如果路径 `segments` 只有一个段,直接在当前节点上设置值。
          node[segments[0]] = value
        }
      }
      // ...(函数的其余部分)
    }
    
    • 方法流程说明:
      1. set 函数接受一个对象 initial,一个路径字符串 path,和一个值 value 作为参数。
      2. 如果 initial 为空,函数返回一个空对象。
      3. 如果 path 为空或 value 未定义,函数返回原始对象 initial
      4. 函数使用正则表达式分割 path 字符串,得到一个表示属性链的数组 segments
      5. 函数定义了一个递归辅助函数 _set,它负责沿着 segments 的路径设置值。
      6. 如果 segments 数组有多个元素,函数递归地创建或获取中间对象,并继续沿着路径设置值。
      7. 如果 segments 数组只有一个元素,函数在当前的节点上设置值。
      8. _set 函数从根对象 initial 开始递归设置值。

shake:过滤对象

  1. 使用说明
    • 功能说明:创建一个新的对象,该对象是原始对象 obj 的副本,但省略了那些经过 filter 函数检查并返回 true 的属性。filter 函数默认会过滤掉值为 undefined 的属性。
    • 参数:目标对象、[过滤函数]。
    • 返回值:过滤完后的映射值组成的数组。
  2. 使用代码示例
    import { shake } from 'radash'
    
    const ra = {
      mode: 'god',
      greek: false,
      limit: undefined
    }
    
    shake(ra) // => { mode, greek }
    shake(ra, a => !a) // => { mode }
    
  3. 源码解析
    // 定义一个泛型函数 `shake`。
    export const shake = <RemovedKeys extends string, T>(
      // 参数 `obj` 是一个对象,其类型为泛型 `T`。
      obj: T,
      // 参数 `filter` 是一个可选的函数,用于决定哪些属性应该被省略。
      // 默认情况下,它会过滤掉值为 `undefined` 的属性。
      filter: (value: any) => boolean = x => x === undefined
    ): Omit<T, RemovedKeys> => {
      // 如果 `obj` 为空,则返回一个空对象。
      // 这里返回的类型应该是 `Omit<T, RemovedKeys>` 而不是 `T`。
      if (!obj) return {} as Omit<T, RemovedKeys>
      // 使用 `Object.keys` 获取 `obj` 的所有键,并将其类型断言为 `T` 的键的类型数组。
      const keys = Object.keys(obj) as (keyof T)[]
      // 使用 `reduce` 方法遍历所有键,创建一个新对象。
      return keys.reduce((acc, key) => {
        // 如果 `filter` 函数对当前键的值返回 `true`,则省略该属性。
        if (filter(obj[key])) {
          return acc
        } else {
          // 否则,将属性添加到累加器对象 `acc` 中。
          acc[key] = obj[key]
          return acc
        }
      }, {} as Omit<T, RemovedKeys>) // 初始累加器是一个空对象,类型为 `Omit<T, RemovedKeys>`。
    }
    
    • 方法流程说明:
      1. shake 函数接受一个对象 obj 和一个可选的过滤函数 filter 作为参数。
      2. 如果 obj 为空,函数返回一个空对象。
      3. 如果 obj 非空,函数使用 Object.keys 获取 obj 的所有键。
      4. 函数使用 reduce 方法遍历键数组。
      5. 对于每个键,函数使用 filter 函数检查对应的值,以确定是否应该省略该属性。
      6. 如果 filter 函数返回 false,函数将该属性及其值添加到累加器对象 acc 中。
      7. 返回包含所选属性的新对象。

写在后面

  • 我相信能看到最后的都是帅气多金想进步的大漂亮和大帅笔,感谢阅读到这儿!
  • 等所有方法更新完毕,作者会整理一份radash完整方法目录上传,包括思维导图和使用目录。
  • 大家有任何问题或见解,欢迎评论区留言交流和批评指正!!!
  • 你的每一个收藏都是作者写作的动力!!!
  • 点击访问:radash官网
05-24 17:00