npm i merge-anything
遞歸合併物件和其他類型。完全支援TypeScript !一個簡單且小型的整合。
我創建這個包是因為我嘗試了很多類似的包來進行合併/深度合併/遞歸對象分配等。
我正在尋找:
Object.assign()
但很深入最後這一點至關重要!當然,在 JavaScript 中幾乎所有東西都是對象,但我不希望合併函數嘗試合併,例如。兩個new Date()
實例!許多庫使用自訂類別來創建具有特殊原型的對象,而這些對像在嘗試合併它們時都會崩潰。所以我們一定要小心!
merge-anything 將合併物件和嵌套屬性,但前提是它們是「普通物件」。一旦子 prop 不是“普通物件”並且具有特殊原型,它就會“按原樣”複製該實例。 ♻️
import { merge } from 'merge-anything'
const starter = { name : 'Squirtle' , types : { water : true } }
const newValues = { name : 'Wartortle' , types : { fighting : true } , level : 16 }
const evolution = merge ( starter , newValues , { is : 'cool' } )
// returns {
// name: 'Wartortle',
// types: { water: true, fighting: true },
// level: 16,
// is: 'cool'
// }
在上面的範例中,如果您使用 TypeScript,並將滑鼠懸停在evolution
上,您實際上可以立即看到新物件的類型。這非常強大,因為您可以合併事物,並且不需要any
,TypeScript 就會準確地知道您新合併的物件的外觀!
merge()
函數的傳回類型也可用作 TypeScript 實用程式:
import type { Merge } from 'merge-anything'
type A1 = { name : string }
type A2 = { types : { water : boolean } }
type A3 = { types : { fighting : boolean } }
type Result = Merge < A1 , [ A2 , A3 ] >
該套件將遞歸地遍歷普通物件並將值合併到新物件上。
請注意,該套件可以識別特殊的 JavaScript 對象,例如類別實例。在這種情況下,它不會像物件一樣遞歸地合併它們,而是「按原樣」將類別分配給新物件!
// all passed objects do not get modified
const a = { a : 'a' }
const b = { b : 'b' }
const c = { c : 'c' }
const result = merge ( a , b , c )
// a === {a: 'a'}
// b === {b: 'b'}
// c === {c: 'c'}
// result === {a: 'a', b: 'b', c: 'c'}
// However, be careful with JavaScript object references with nested props. See below: A note on JavaScript object references
// arrays get overwritten
// (for "concat" logic, see Extensions below)
merge ( { array : [ 'a' ] } , { array : [ 'b' ] } ) // returns {array: ['b']}
// empty objects merge into objects
merge ( { obj : { prop : 'a' } } , { obj : { } } ) // returns {obj: {prop: 'a'}}
// but non-objects overwrite objects
merge ( { obj : { prop : 'a' } } , { obj : null } ) // returns {obj: null}
// and empty objects overwrite non-objects
merge ( { prop : 'a' } , { prop : { } } ) // returns {prop: {}}
merge-anything 正確地保持特殊物件的完整性,如日期、正規表示式、函數、類別實例等。
然而,了解如何解決 JavaScript 物件參考非常重要。請務必閱讀下面關於 JavaScript 物件參考的#a note。
預設行為是覆蓋數組。如果需要連接數組,可以匯入mergeAndConcat
。但如果您不需要這個,請不要擔心,這個庫是可搖樹的,並且不會導入您不使用的程式碼!
import { mergeAndConcat } from 'merge-anything'
mergeAndConcat (
{ nested : { prop : { array : [ 'a' ] } } } ,
{ nested : { prop : { array : [ 'b' ] } } }
)
// returns { nested: { prop: { array: ['a', 'b'] } } },
當兩個事物合併時,有時您可能需要調整邏輯。您可以提供自己的自訂函數,每次覆蓋值時都會觸發該函數。
對於本例,我們使用mergeAndCompare
。下面是一個使用比較函數連接字串的範例:
import { mergeAndCompare } from 'merge-anything'
function concatStrings ( originVal , newVal , key ) {
if ( typeof originVal === 'string' && typeof newVal === 'string' ) {
// concat logic
return ` ${ originVal } ${ newVal } `
}
// always return newVal as fallback!!
return newVal
}
mergeAndCompare ( concatStrings , { name : 'John' } , { name : 'Simth' } )
// returns { name: 'JohnSmith' }
TypeScript 用戶請注意。此函數傳回的類型可能不正確。在這種情況下,您必須將結果轉換為您自己提供的接口
請小心 JavaScript 物件引用。任何嵌套的屬性都將是反應性的,並在原始物件和合併物件之間連結!下面我們將展示如何防止這種情況。
const original = { airport : { status : 'dep. ?' } }
const extraInfo = { airport : { location : 'Brussels' } }
const merged = merge ( original , extraInfo )
// we change the status from departuring ? to landing ?
merged . airport . status = 'lan. ?'
// the `merged` value will be modified
// merged.airport.status === 'lan. ?'
// However `original` value will also be modified!!
// original.airport.status === 'lan. ?'
要記住的關鍵規則是:
任何嵌套超過 1 層且沒有重疊父屬性的屬性都將在合併結果和來源中進行回應和連結
然而,有一個非常簡單的解決方案。我們可以複製合併結果來消除任何反應性。為此,我們可以使用複製任何內容庫。該庫還確保特殊類別實例不會損壞,因此您可以使用它而不必擔心損壞東西!
請參閱下面我們如何整合“複製任何內容”:
import { copy } from 'copy-anything'
const original = { airport : { status : 'dep. ?' } }
const extraInfo = { airport : { location : 'Brussels' } }
const merged = copy ( merge ( original , extraInfo ) )
// we change the status from departuring ? to landing ?
merged . airport . status = 'lan. ?' ( merged . airport . status === 'lan. ?' ) (
// true
// `original` won't be modified!
original . airport . status === 'dep. ?'
) // true
然後,您可以嘗試放置copy()
函數的位置。
Copy Anything 也完全支援 TypeScript!
它實際上只是遞歸地遍歷一個物件並將值指派給一個新對象,如下所示。但是,它被包裝以允許額外的參數等。
import { isPlainObject } from 'is-what'
function mergeRecursively ( origin , newComer ) {
if ( ! isPlainObject ( newComer ) ) return newComer
// define newObject to merge all values upon
const newObject = isPlainObject ( origin )
? Object . keys ( origin ) . reduce ( ( carry , key ) => {
const targetVal = origin [ key ]
if ( ! Object . keys ( newComer ) . includes ( key ) ) carry [ key ] = targetVal
return carry
} , { } )
: { }
return Object . keys ( newComer ) . reduce ( ( carry , key ) => {
const newVal = newComer [ key ]
const targetVal = origin [ key ]
// early return when targetVal === undefined
if ( targetVal === undefined ) {
carry [ key ] = newVal
return carry
}
// When newVal is an object do the merge recursively
if ( isPlainObject ( newVal ) ) {
carry [ key ] = mergeRecursively ( targetVal , newVal )
return carry
}
// all the rest
carry [ key ] = newVal
return carry
} , newObject )
}
* 當然,與實際原始程式碼存在細微差別,以應對罕見情況和額外功能。實際的源代碼在這裡。