Skip to content

merge

merge 对象合并

用于将源对象的属性合并到目标对象,支持深度合并普通对象,数组可选择替换或拼接,默认不会修改原对象(返回新对象)。

一、引入与使用

支持两种方式引入:

ts
// 方式一:按需子路径引入(推荐)
import { merge } from '@cuixingjian/cui-utils/merge'

// 方式二:从包入口命名导出引入
import { merge } from '@cuixingjian/cui-utils'

二、使用示例

ts
import { merge } from '@cuixingjian/cui-utils/merge'

const target = { a: 1, user: { name: 'cui', tags: ['ui'] } }
const source = { b: 2, user: { age: 20, tags: ['framework'] } }

// 深度合并,数组默认替换(replace)
const result1 = merge(target, source)
// result1: { a: 1, b: 2, user: { name: 'cui', age: 20, tags: ['framework'] } }

// 指定数组拼接(concat),不修改原对象(immutable: true)
const result2 = merge(target, source, { arrayStrategy: 'concat' })
// result2: { a: 1, b: 2, user: { name: 'cui', age: 20, tags: ['ui', 'framework'] } }

// 关闭深度合并,只进行浅覆盖
const result3 = merge(target, source, { deep: false })
// result3: { a: 1, b: 2, user: { age: 20, tags: ['framework'] } }

三、API

方法说明类型
merge将源对象属性合并到目标对象并返回合并结果(target: object, source: object, options?) => object

参数

参数名说明类型默认值
target目标对象object
source源对象object
options合并选项MergeOptions{ deep: true, arrayStrategy: 'replace', immutable: true }

MergeOptions

字段说明类型默认值
deep是否进行深度合并(递归普通对象)booleantrue
arrayStrategy数组合并策略:replace 替换;concat 拼接`'replace''concat'`
immutable是否返回新对象(不修改原对象)booleantrue

返回值

名称说明类型
result合并后的新对象object

四、注意事项

  • 仅对“普通对象”(原型为 Object.prototypenull)进行深度合并;其他类型(如 DateRegExpMapSet、函数等)直接覆盖引用。
  • 数组默认替换,可通过 arrayStrategy: 'concat' 拼接;不做去重处理。
  • 默认不修改目标对象(immutable: true);如需在原对象上合并可设置为 false

五、源码

展开查看
ts
// 源码来自 @cuixingjian/cui-utils/merge
export type AnyObject = Record<string | symbol, any>

export interface MergeOptions {
  deep?: boolean
  arrayStrategy?: 'replace' | 'concat'
  immutable?: boolean
}

function isPlainObject(val: any): val is AnyObject {
  if (val === null || typeof val !== 'object') return false
  const proto = Object.getPrototypeOf(val)
  return proto === Object.prototype || proto === null
}

export function merge<T extends AnyObject>(
  target: T,
  source: AnyObject,
  options: MergeOptions = {}
): T {
  const { deep = true, arrayStrategy = 'replace', immutable = true } = options
  const out: AnyObject = immutable ? { ...target } : (target as AnyObject)
  const keys = Reflect.ownKeys(source)
  for (const key of keys) {
    const sVal = (source as any)[key]
    const tVal = (out as any)[key]
    if (Array.isArray(sVal)) {
      if (arrayStrategy === 'concat') {
        const base = Array.isArray(tVal) ? tVal.slice() : []
        ;(out as any)[key] = base.concat(sVal.map((v) => (isPlainObject(v) ? { ...v } : v)))
      } else {
        ;(out as any)[key] = sVal.map((v) => (isPlainObject(v) ? { ...v } : v))
      }
      continue
    }
    if (deep && isPlainObject(sVal) && isPlainObject(tVal)) {
      ;(out as any)[key] = merge({ ...tVal }, sVal, { deep, arrayStrategy, immutable: false })
      continue
    }
    ;(out as any)[key] = sVal
  }
  return out as T
}