deepmerge-plus
v4.0.1
Published
用於深度(遞迴)合併 JavaScript 物件的函式庫 / A library for deep (recursive) merging of JavaScript objects
Maintainers
Readme
deepmerge-plus
深度合併兩個 JavaScript 物件的可列舉屬性 / Merge the enumerable attributes of two objects deeply.
pnpm add deepmerge-plus
概述
deepmerge-plus 是一個功能強大的 JavaScript 物件深度合併函式庫,支援:
- � depth-first 深度合併(遞迴處理巢狀物件)
- 📦 陣列合併(可自訂合併策略)
- ⚙️ 靈活的選項配置
- 🔧 自訂合併邏輯
安裝
# 使用 pnpm(推薦)
pnpm add deepmerge-plus
# 使用 npm
npm install deepmerge-plus
# 使用 yarn
yarn add deepmerge-plus快速開始
const merge = require('deepmerge-plus');
const target = {
foo: { bar: 5 },
array: [{ does: 'work', too: [1, 2, 3] }]
};
const source = {
foo: { baz: 4 },
quux: 5,
array: [{ does: 'work', too: [4, 5, 6] }, { really: 'yes' }]
};
const result = merge(target, source);
console.log(result);
// => { foo: { bar: 5, baz: 4 }, array: [...], quux: 5 }核心概念:左至右合併
在 merge(target, source, options) 中:
| 參數 | 術語 | 說明 |
|-----|------|------|
| target | 左側(left)| 被合併的物件,代表現有值 |
| source | 右側(right)| 要合併進來的物件,代表新值 |
預設行為
- 來源物件的值會覆蓋目標物件的值
- 合併結果是將「右側」的值合併進「左側」
- 合併會建立一個新物件,因此
target和source都不會被修改
const target = { name: 'Alice', age: 25 };
const source = { name: 'Bob', city: 'Taipei' };
const result = merge(target, source);
// => { name: 'Bob', age: 25, city: 'Taipei' }
// name 被 source 覆蓋,age 保留 target,city 來自 sourceAPI
merge(target, source, [options])
深度合併兩個物件 target 和 source,返回一個包含兩者元素的新合併物件。
const result = merge(objectA, objectB, options);merge.all(arrayOfObjects, [options])
將任意數量的物件合併成單一結果物件。
const result = merge.all([obj1, obj2, obj3], options);選項說明
1. clone
是否啟用深度複製功能。
- 類型:
boolean - 預設值:
true
merge(target, source, { clone: true }); // 預設,深度複製物件
merge(target, source, { clone: false }); // 不複製,直接引用2. arrayMerge
自訂陣列合併函式。
- 類型:
function(target, source, options): array - 預設行為: 串接兩個陣列
// 覆寫模式:來源陣列覆蓋目標陣列
function overwriteMerge(destinationArray, sourceArray) {
return sourceArray;
}
merge([1, 2, 3], [3, 2, 1], { arrayMerge: overwriteMerge });
// => [3, 2, 1]// 僅保留目標陣列
const keepTarget = (destination) => destination;
merge({ arr: [1,2,3] }, { arr: ['a','b','c'] }, { arrayMerge: keepTarget });
// => { arr: [1, 2, 3] }3. isMergeableObject
自訂可合併物件判斷函式。
- 類型:
function(value): boolean
const moment = require('moment');
merge(target, source, {
isMergeableObject(value) {
if (moment.isMoment(value)) return false;
return isMergeableObject(value);
}
});4. keyValueOrMode(已棄用)
使用 || 運算子邏輯,已由 keyValueUpsertMode 取代。
- 類型:
boolean - 狀態: 已棄用,不建議在新程式碼中使用
5. keyValueUpsertMode(推薦)
Upsert 模式,控制何時應該保留目標中的現有值。
- 類型:
boolean或function - 預設值:
undefined
為 true 時
保留目標中已存在的值(undefined 除外)。
const target = { name: 'Alice', age: undefined, count: 0, email: null };
const source = { name: 'Bob', age: 30, count: 5, email: '[email protected]' };
const result = merge(target, source, { keyValueUpsertMode: true });
// => { name: 'Alice', age: 30, count: 0, email: null }
// name: 保留 'Alice'(不是 undefined)
// age: 使用 30(目標值是 undefined)
// count: 保留 0(不是 undefined)
// email: 保留 null(不是 undefined)關鍵特性:
- 使用
??運算子,區分undefined與其他 falsy 值 null、0、false、''都會被保留- 與 lodash 的
_.defaultsDeep概念相似
為 function 時
自訂函式,根據條件決定是否保留目標的值。
const target = { name: 'Alice', count: 0 };
const source = { name: 'Bob', count: 5 };
const result = merge(target, source, {
keyValueUpsertMode: (value, options, tmpRuntimeTarget) => {
const targetValue = tmpRuntimeTarget.target?.[tmpRuntimeTarget.key];
return targetValue === 0; // 只有當目標值為 0 時保留
}
});
// => { name: 'Bob', count: 0 }
// name: targetValue='Alice' !== 0,回傳 false,使用 source 的 'Bob'
// count: targetValue=0 === 0,回傳 true,保留 target 的 0函式參數說明:
| 參數 | 類型 | 說明 |
|-----|------|------|
| value | unknown | 來源物件中該鍵的值 |
| optionsRuntime | IOptions | 目前的合併選項 |
| tmpRuntimeTarget | ICache | 包含 target、source、destination、key 等資訊 |
| tmpRuntimeData | ITmpRuntimeData | 包含 level、paths、root、parent 等資訊 |
進一步閱讀
如需了解 keyValueUpsertMode 與陣列合併策略的詳細組合應用,請參閱:
實際應用場景
1. 配置合併(保留使用者設定)
// 預設配置
const defaultConfig = {
theme: 'light',
language: 'en',
notifications: { email: true, sms: false },
timeout: 3000
};
// 使用者自訂配置
const userConfig = {
theme: 'dark',
notifications: { email: false }
};
// 合併:保留使用者設定,填充預設值
const finalConfig = merge(defaultConfig, userConfig, {
keyValueUpsertMode: true
});
// => { theme: 'dark', language: 'en', notifications: { email: false }, timeout: 3000 }對比 lodash:
_.defaults(userConfig, defaultConfig)- 填充缺失的鍵merge(defaultConfig, userConfig, { keyValueUpsertMode: true })- 保留使用者設定
2. 表單資料合併
// 預設表單值
const defaultValues = {
username: '',
password: '',
rememberMe: false,
preferences: { newsletter: true, language: 'en' }
};
// 使用者已輸入的資料
const userInput = {
username: 'alice',
preferences: { language: 'zh-TW' }
};
// 合併:保留使用者已輸入的資料
const formData = merge(defaultValues, userInput, {
keyValueUpsertMode: true
});
// => { username: 'alice', password: '', rememberMe: false, preferences: { newsletter: true, language: 'zh-TW' } }3. 環境變數覆蓋
// 基礎環境配置
const baseConfig = {
API_URL: 'https://api.example.com',
DEBUG: false,
CACHE: { enabled: true, ttl: 3600 }
};
// 開發環境配置
const devConfig = {
DEBUG: true,
API_URL: 'https://dev-api.example.com'
};
// 生產環境配置
const prodConfig = {
DEBUG: false
};
// 合併環境配置(保留最基礎的值)
const envConfig = merge(baseConfig, devConfig, {
keyValueUpsertMode: true
});
// => { API_URL: 'https://dev-api.example.com', DEBUG: true, CACHE: { enabled: true, ttl: 3600 } }4. 深度合併(類似 _.merge)
const target = { user: { name: 'Alice', age: 25 }, role: 'admin' };
const source = { user: { name: 'Bob', city: 'Taipei' }, role: 'user' };
// 預設深度合併(source 覆蓋 target)
const result1 = merge(target, source);
// => { user: { name: 'Bob', age: 25, city: 'Taipei' }, role: 'user' }
// 使用 keyValueUpsertMode 保留 target 的值
const result2 = merge(target, source, { keyValueUpsertMode: true });
// => { user: { name: 'Alice', age: 25, city: 'Taipei' }, role: 'admin' }與 lodash 函式對比
| lodash 函式 | deepmerge-plus 實作 | 說明 |
|------------|-------------------|------|
| _.defaults | merge(target, source, { keyValueUpsertMode: true }) | 淺層預設值(需正確參數順序) |
| _.defaultsDeep | merge(target, source, { keyValueUpsertMode: true }) | 深度預設值 |
| _.merge | merge(target, source) | 預設深度合併 |
重要語義差異:
_.defaults/_.defaultsDeep:填充缺失的鍵(fill missing)keyValueUpsertMode: true:有值則保留(preserve if exists)
類型定義
interface IOptions {
/** 是否啟用深度複製,預設 true */
clone?: boolean;
/** 自訂陣列合併函式 */
arrayMerge?: <T extends any[]>(target: T, source: any[], options?: IOptions) => T[];
/** 自訂可合併物件判斷函式 */
isMergeableObject?: (value: any) => boolean;
/** 已棄用:使用 keyValueUpsertMode 取代 */
keyValueOrMode?: boolean;
/** Upsert 模式:true 或自訂函式 */
keyValueUpsertMode?:
| boolean
| ((value: unknown, optionsRuntime?: IOptions, tmpRuntimeTarget?: ICache) => boolean);
}測試
pnpm test授權
MIT
