fastextend
Fast recursive object and array clone and merge focused on literal js types. Optimized for v8.
npm install fastextend
This module is aimed at solving two common use-cases, deep cloning objects/arrays and merging objects and arrays. Many of the current modules suffer from v8 deopts or wonky behaviors regarding arrays, undefined and nested non-literal objects.
Careful work is taken on the internals in order to make sure that we are staying monomorphic and avoiding v8 de-opts according to some of the work by Vyacheslav Egorov. See What's up with monomorphism for more info. This allows us to get 3x to 10x performance over common libraries methods lodash
, fast-clone
, extend
or JSON.parse(JSON.stringify(obj))
.
Right now fastextend
only works on simple javascript natives, the kind that JSON supports, because attempting to extend/clone non-literal objects is a minefield and causes almost every deep-extend module to have unsolvable edgecases.
cloneable values: {}, [], string, number, boolean, null, undefined.
benchmark
Run the benchmark via npm run simplebench
.
As of 8/11/2017 on Node 7.10.1, higher ops/sec is better.
Group: defaultWinner - fastextendclone fastextendclone - count: 176450 ops/sec: 176450JSON dance - count: 60164 ops/sec: 60164 diff: -6590%extend - count: 37990 ops/sec: 37990 diff: -7847%fast-clone - count: 29268 ops/sec: 29268 diff: -8341%lodashcloneDeep - count: 18203 ops/sec: 18203 diff: -8968%
Getting Started
// clone an objectvar obj = fastextend; // merge multiple objectsvar obj = fastextend; // deep clone arrays of objects, all nested objects and arrays are clonedvar arr = fastextend;
fastextend.clone(obj)
Deep clones an object or an array. The object/array must only contain clonable values. Non-clonable values such as non-literal Objects, Dates or functions will throw.
This is just a shortcut for fastextend.merge({}, obj)
or fastextend.merge([], arr)
.
// deep clones the object to foovar foo = fastextend; // deep clones an array of objects, all arrays and objects are clonedvar foo = fastextend;
fastextend.merge(target, arg1, arg2, argN);
Clones of each argument are merged into the target object. No interim objects/arrays are modified.
Caveats
- You can pass as many objects/arrays and will merge them left to right into the first argument. Nothing but the first argument are modified.
- A key with
undefined
value will overwrite a key with a value. If the key exists it merges. - If the merging key contains an array or object and the target key is neither, it will be overwritten. If the target key matches the type, the subkeys are merged in.
- Merging arrays overwrites by key. So
fastextend.merge([1,2], [5,2,3], [4]) === [4,2,3]
. - If merge-key is not clonable, it will throw.
Examples
// merging arrays of objectsfastextend// result id : 1 categories : catid : 1 added : 1 // merge undefined overwriting key with valuefastextend// result foo : undefined
fastextend.mergeWithOptions(target, arg1, arg2, argN, options);
Merge but with the latest argument being options
.
options.mergeUndefined
- Defaulttrue
. If true, merges will copy undefined values right to left. So an undefined value will overwrite a value. If set to false, undefined values will not merge.options.mergeArrays
- Defaulttrue
. If true, it will recurse into arrays and merge keys into those arrays. If false, it treats arrays as simple key, and will only deep clone the right key, but will not merge entries with the left key. In some cases it is desirable that if an array exists in the right key that it simply replaces the array on the left, rather than blends. Setting this to false will accomplish that.
fastextend;// result foo : "fooValue" baz : null fastextend; foo : undefined bar : undefined baz : null fastextend; foo : baz : 1 fastextend; foo : bar : 1 baz : 1 bar : 2