An isomorphic and configurable javascript utility for objects deep cloning that supports circular references.

Overview

NPM version license

omniclone

An isomorphic and configurable javascript function for object deep cloning.

omniclone(source [, config, [, visitor]]);

Example:

const obj = { foo: { bar: 'baz' } };
const obj2 = omniclone(obj);

obj2; // { foo: { bar: 'baz' } };
obj === obj2; // false
obj.foo === obj2.foo; // false

installation

$ npm install --save omniclone

usage

const omniclone = require('omniclone');
import omniclone from 'omniclone';

test

Test it in no time with RunKit!

strengths

  1. automatically invoke object constructors before copying properties (customizable behavior)
  2. let you to share the [[Prototype]] object between source and the resulting object (customizable behavior)
  3. let you to clone objects with circular references (customizable behavior)
  4. let you to copy getters and setters, non enumerables properties and also symbols (customizable behavior)
  5. correct handling of String, Boolean, Number, Error, Promise, Map, Set, WeakMap, WeakSet, ArrayBuffer, TypedArray and DataView objects
  6. similar references are not duplicated
  7. correct cloning of Array objects
  8. correct cloning of RegExp and Date objects
  9. let you define custom cloning logic

config

invokeConstructors (default true)

If you need to invoke the objects constructors for each object prop set the invokeConstructors flag to true:

const res = omniclone(source, {
  invokeConstructors: true
});

This option will correctly set up the new object, because constructors are invoked to create it. The resulting object and each of its object property therefore will have the [[Prototype]] and the constructor props correctly setted up, corresponding to the source object and its object properties for everyone.

class Test {
  constructor() {
    console.log('constructor invoked');
  }
};

const t = new Test(); // 'constructor invoked'
t.foo = new Test(); // 'constructor invoked'
t; // Test { t: Test {} }

const res = omniclone(t, {
  invokeConstructors: true
}); // 'constructor invoked' 'constructor invoked'

res; // Test { t: Test {} }
res instanceof Test; // true
res.foo instanceof Test; // true

It is actually a default enabled setting, but you can disable it (Array, Map and Set objects will be properly cloned anyway).
It is highly discouraged to disable this flag, do it only if you know what you are doing.

If the invokeConstructors flag is set to false, a plain new object will be created for each object prop and for the resulting object as well. So the constructor prop will be set to the Object function, and the [[Prototype]] prop will be Object.prototype.
Unless you use the setPrototype flag.

setPrototype (default false)

If the invokeConstructors flag is setted to false we could anyway share the [[Prototype]] object between the source object and the resulting object thanks to the setPrototype flag, without calling the constructors.
(Array, Map and Set objects will be properly cloned anyway because for them the constructor will be always called).
This means that the constructor prop will be shared as well because it is related to the [[Prototype]] prop.
This flag affects all the object properties as weel, like the previous flag.
If the invokeConstructors flag is setted to true, the setPrototype flag will be is ignored.

const res = omniclone(source, {
  invokeConstructors: false,
  setPrototype: true
});

The resulting object therefore will have the [[Prototype]] and the constructor props correctly setted up, but the constructors are not invoked.

class Test {
  constructor() {
    console.log('constructor invoked');
  }
};

const t = new Test(); // 'constructor invoked'
t.foo = new Test(); // 'constructor invoked'
t; // Test { t: Test {} }

const res = omniclone(t, {
  invokeConstructors: false,
  setPrototype: true
});

res; // Test { t: Test {} }
res instanceof Test; // true
res.foo instanceof Test; // true
const prot = { foo:'bar' };

const obj1 = Object.create(prot);
Object.getPrototypeOf(obj1) === prot; // true

const res = omniclone(obj1, {
  invokeConstructors: false,
  setPrototype: true
});
Object.getPrototypeOf(res) === prot; // true

copyNonEnumerables (default false)

Enable it to deep copy also non enumerables properties.
Disable it to ignore them.

const res = omniclone(source, {
  copyNonEnumerables: true
});

copySymbols (default false)

Enable it to deep copy also symbol properties.
Disable it to ignore them.
Symbols are shallow copied;

const res = omniclone(source, {
  copySymbols: true
});

copyGettersSetters (default false)

Enable it to copy also getters and setters.
Disable it to ignore them.

const res = omniclone(source, {
  copyGettersSetters: true
});

Odds are that to properly copy gets&setts you have also to enable the copyNonEnumerables flag.

allowCircularReferences (default true)

Enable it to allow circular references.
Disable it to throw an error if one is met. Know that omniclone is more performing with this flag enabled, so disable it only if you really need.

const res = omniclone(source, {
  allowCircularReferences: true
});

discardErrorObjects (default true)

Enable it to discard Error objects props.
Know that omnicopy will return null if one is passed as source.
Disable it to throw an error if one is met.

const res = omniclone(source, {
  discardErrorObjects: true
});

default config

The default config is the following:

omniclone(source, {
    invokeConstructors : true,
    setPrototype : false,
    copyNonEnumerables : false,
    copySymbols : false,
    copyGettersSetters : false,
    allowCircularReferences: true,
    discardErrorObjects: true,
});

what about String, Boolean, Number, Error, Promise, Map, Set, WeakMap, WeakSet, ArrayBuffer, TypedArray and DataView objects?

String, Boolean and Number objects passed to omniclone as sources will produce null.
Error objects passed to omniclone as sources will produce null if the discardErrorObjects is set to true (as default).
Error objects passed to omniclone as sources will produce a TypeError if the discardErrorObjects is set to false (not the predefined behaviour).

String, Boolean and Number objects props will be unwrapped.
Error objects props will be discarded if the discardErrorObjects is set to true (as default).
Error objects props will produce a TypeError if the discardErrorObjects is set to false (not the predefined behaviour).

Promise, WeakMap and WeakSet objects will be returned if passed to omniclone as sources.
Promise, WeakMap and WeakSet objects props will be copied by reference.

Map objects (keys/values) will be always deeply cloned, but any properties added to the map object itself will not be copied.
Set objects will be always deeply cloned, but any properties added to the set object itself will not be copied.
ArrayBuffer and TypedArray objects will be always deeply cloned, but any properties added to the array objects themselves will not be copied. DataView objects are copied by reference.

what about the 6th strength?

To understand it, let's compare the function omniclone with the well-know JSON.parse(JSON.stringify(source)):

const obj = { foo: 'bar'};
const source = {
  a: obj,
  b: obj,
};

JSON.stringify(source); // '{"a":{"foo":"bar"},"b":{"foo":"bar"}}'

When you will use JSON.parse(), an {"foo":"bar"} object will be created for the a prop and a {"foo":"bar"} distinct object will be created for the b prop. But this is not the initial situation where source.a == source.b; // true.

how to define custom cloning logic?

You can define a callback function that will be called on each node which copy can be customized:

function visitor(node, config) {
  // do stuff
}

The function will receive the node and a copy of the config object. If the function returns something different than undefined, that returned value will become the cloned value. On the contrary if the function returns undefined the default cloning algorithm will be used.

You cannot overwrite the default algorithm logic for String, Boolean, Number, Error, Promise, Map, Set, WeakMap, WeakSet, Date and RegExp objects. You can overwrite the default algorithm logic for Array, ArrayBuffer, TypedArray, DataView, plain js and custom objects.

Let's see an example where we add custom logic to properly clone a Node.js Buffer object:

const buffer = Buffer.from([1, 2, 3, 4]);

const resBuffer = omniclone(buffer, {}, node => {
  if (node instanceof Buffer) { // it affects only Buffer objects
    return Buffer.from(node);
  }
  return undefined; // all the other nodes will be cloned as usual
});

Thanks to the instanceof check, only Buffer objects will be affected by the intervention of the visitor callback.

warnings and limitations

  1. promises and methods are always copied by reference
  2. super is statically bound to a class heirarchy, remember it
  3. Error objects cannot be properly copied because of js limitations
  4. currently there is no isomorphic way to detect if an object is a Proxy nor is possible to access the handler object. Because of transparent virtualization, omniclone will copy each properties, the constructor and the [[Prototype]] directly from the proxed object.

support

Let me know that you like this project: star it! Thanks :)

You might also like...

A tiny JavaScript utility to access deep properties using a path (for Node and the Browser)

object-path Access deep properties using a path Changelog 0.11.5 SECURITY FIX. Fix a prototype pollution vulnerability in the set() function when usin

Dec 29, 2022

Hemsida för personer i Sverige som kan och vill erbjuda boende till människor på flykt

Getting Started with Create React App This project was bootstrapped with Create React App. Available Scripts In the project directory, you can run: np

May 3, 2022

Kurs-repo för kursen Webbserver och Databaser

Webbserver och databaser This repository is meant for CME students to access exercises and codealongs that happen throughout the course. I hope you wi

Jan 3, 2023

A tiny (304B to 489B) utility to check for deep equality

dequal A tiny (304B to 489B) utility to check for deep equality This module supports comparison of all types, including Function, RegExp, Date, Set, M

Dec 14, 2022

Serialize and deserialize any object and all of its references 🥣

Super Cereal 🥣 Serialize and deserialize any object and all of its references. Supports: Class (with inheritance set-up) Object Array Function Map Se

Dec 24, 2022

Serialize and deserialize any object and all of its references 🥣

Super Cereal 🥣 Serialize and deserialize any object and all of its references. Supports: Class (with inheritance set-up) Object Array Function Map Se

Nov 1, 2022

Chappe - 🧑‍💻 Developer Docs builder. Write guides in Markdown and references in API Blueprint. Comes with a built-in search engine.

Chappe - 🧑‍💻 Developer Docs builder. Write guides in Markdown and references in API Blueprint. Comes with a built-in search engine.

Chappe Developer Docs builder. Write guides in Markdown and references in API Blueprint. Comes with a built-in search engine. Chappe is a Developer Do

Jan 1, 2023

🧑‍💻 Developer Docs builder. Write guides in Markdown and references in API Blueprint. Comes with a built-in search engine.

🧑‍💻 Developer Docs builder. Write guides in Markdown and references in API Blueprint. Comes with a built-in search engine.

Developer Docs builder. Write guides in Markdown and references in API Blueprint. Comes with a built-in search engine. Chappe is a Developer Docs buil

Jan 1, 2023

This Plugin is For Logseq. If you're using wide monitors, you can place journals, linked references, and journal queries side by side.

This Plugin is For Logseq. If you're using wide monitors, you can place journals, linked references, and journal queries side by side.

Logseq Column-Layout Plugin Journals, linked references, and journal queries can be placed side by side if the minimum screen width is "1850px" or mor

Dec 14, 2022

This Repository consist of Daily learning JS, Assignments, coding challenge, projects, references, tutorial

This Repository consist of Daily learning JS, Assignments, coding challenge, projects, references, tutorial

💛 A Tour of JavaScript This Repository consist of Daily learning, Assignments, coding challenge, projects, references, tutorial. 👩‍💻 👨‍💻 alert(

Sep 7, 2022

🧩 TypeScript utility type in order to ensure to return only properties (not methods) containing values in primitive types such as number or boolean (not Value Objects)

🧩 TypeScript utility type in order to ensure to return only properties (not methods) containing values in primitive types such as number or boolean (not Value Objects)

🧩 TypeScript Primitives type TypeScript utility type in order to ensure to return only properties (not methods) containing values in primitive types

Dec 7, 2022

a tiny and isomorphic URL router for JavaScript

a tiny and isomorphic URL router for JavaScript

Synopsis Director is a router. Routing is the process of determining what code to run when a URL is requested. Motivation A routing library that works

Dec 28, 2022

CASL is an isomorphic authorization JavaScript library which restricts what resources a given user is allowed to access

CASL is an isomorphic authorization JavaScript library which restricts what resources a given user is allowed to access

CASL (pronounced /ˈkæsəl/, like castle) is an isomorphic authorization JavaScript library which restricts what resources a given user is allowed to ac

Dec 31, 2022

:seedling: Next-Gen AI-Assisted Isomorphic Application Engine for Embedded, Console, Mobile, Server and Desktop

lychee.js Mono Repository Important Notes to follow through Installation Quirks: The lycheejs-engine Repository needs to be installed to the path /opt

Dec 31, 2022

Catberry is an isomorphic framework for building universal front-end apps using components, Flux architecture and progressive rendering.

Catberry is an isomorphic framework for building universal front-end apps using components, Flux architecture and progressive rendering.

Catberry What the cat is that? Catberry was developed to help create "isomorphic/Universal" Web applications. Long story short, isomorphic/universal a

Dec 20, 2022

React Starter Kit — isomorphic web app boilerplate (Node.js, Express, GraphQL, React.js, Babel, PostCSS, Webpack, Browsersync)

React Starter Kit — isomorphic web app boilerplate (Node.js, Express, GraphQL, React.js, Babel, PostCSS, Webpack, Browsersync)

React Starter Kit — "isomorphic" web app boilerplate React Starter Kit is an opinionated boilerplate for web development built on top of Node.js, Expr

Dec 30, 2022

React Starter Kit — isomorphic web app boilerplate (Node.js, Express, GraphQL, React.js, Babel, PostCSS, Webpack, Browsersync)

React Starter Kit — isomorphic web app boilerplate (Node.js, Express, GraphQL, React.js, Babel, PostCSS, Webpack, Browsersync)

React Starter Kit — "isomorphic" web app boilerplate React Starter Kit is an opinionated boilerplate for web development built on top of Node.js, Expr

Jan 1, 2023
Releases(0.7.0)
Owner
Andrea Simone Costa
Hey there! I am using GitHub 😄😄😄
Andrea Simone Costa
HashMap JavaScript class for Node.js and the browser. The keys can be anything and won't be stringified

HashMap Class for JavaScript Installation Using npm: $ npm install hashmap Using bower: $ bower install hashmap You can download the last stable ver

Ariel Flesler 381 Sep 24, 2022
Immutable persistent data collections for Javascript which increase efficiency and simplicity.

Immutable collections for JavaScript Immutable data cannot be changed once created, leading to much simpler application development, no defensive copy

Immutable.js 32.4k Dec 31, 2022
ClojureScript's persistent data structures and supporting API from the comfort of vanilla JavaScript

mori A simple bridge to ClojureScript's persistent data structures and supporting APIs for vanilla JavaScript. Pull requests welcome. Breaking changes

David Nolen 3.4k Dec 31, 2022
A complete, fully tested and documented data structure library written in pure JavaScript.

Buckets A JavaScript Data Structure Library Buckets is a complete, fully tested and documented data structure library written in pure JavaScript. Incl

Mauricio 1.2k Jan 4, 2023
Immutable persistent data collections for Javascript which increase efficiency and simplicity.

Immutable collections for JavaScript Immutable data cannot be changed once created, leading to much simpler application development, no defensive copy

Immutable.js 32.4k Jan 7, 2023
Hjson for JavaScript

hjson-js Hjson, a user interface for JSON JSON is easy for humans to read and write... in theory. In practice JSON gives us plenty of opportunities to

Hjson 387 Dec 20, 2022
Fast, deep, scalable object cloning for JavaScript

A new approach to deep cloning. A simple library with no external dependencies gives you the opportunity to customize the cloning strategy.

Egor Efimenko 3 Mar 10, 2022
A utility for cloning all your repos, including issues, discussions, stargazers and more!

github-takeout A utility for cloning all your repos, including issues, discussions, stargazers and more! The tool gives you the ability to download a

Lukas Bach 5 Oct 26, 2022
Build a fake backend by providing the content of JSON files or JavaScript objects through configurable routes.

http-fake-backend Build a fake backend by providing the content of JSON files or JavaScript objects through configurable routes. It actually can serve

Micromata GmbH 279 Dec 11, 2022
Grupprojekt för kurserna 'Javascript med Ramverk' och 'Agil Utveckling'

JavaScript-med-Ramverk-Laboration-3 Grupprojektet för kurserna Javascript med Ramverk och Agil Utveckling. Utvecklingsguide För information om hur utv

Svante Jonsson IT-Högskolan 3 May 18, 2022