packages/deep-clone/README.md
# @jsbits/deep-clone
_Part of the [JSBits][jsbits-url] suite._
[![License][license-badge]](LICENSE)
[![npm Version][npm-badge]][npm-url]
[![minified size][size-badge]][size-url]<br>
[![AppVeyor Test][appveyor-badge]][appveyor-url]
[![Travis Test][travis-badge]][travis-url]
[![coverage][codecov-badge]][codecov-url]
[![code quality][codacy-badge]][codacy-url]
[![maintainability][climate-badge]][climate-url]
Performs a deep cloning of an object own properties and symbols, with loosy or exact behavior.
## Install
For NodeJS and JS bundlers:
```bash
npm i @jsbits/deep-clone
# or
yarn add @jsbits/deep-clone
```
or load `deepClone` in the browser:
```html
<script src="https://unpkg.com/@jsbits/deep-clone/index.b.min.js"></script>
```
### Targets
- ES5 compatible browser
- NodeJS v4.2 or later
## `deepClone(value, [exact])` ⇒ `T`
Performs a deep cloning of an object own properties and symbols, preserving
its prototype.
By default `cloneObject` works in "loosy mode", where it clones only
the object _enumerable_ properties and symbols.
To enable the "exact mode" and clone all, pass `true` in the second parameter.
Both modes retain all the attributes of the copied properties (enumerable,
configurable, writable) and correctly transfer the `get` and/or `set`
methods, although these, like the other function-type values,
_are copied by reference_.
Try to limit the usage of this function to POJOs, as this function does not
work for objects with constructor that requires parameters (other than
the most JS built-in Objects), nor objects with recursive references.
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| value | `T` | | Value to clone, mostly an object or array. |
| \[exact] | `boolean` | `false` | If `true`, also clone the non-enumerable properties |
**Returns**: `T` - The cloned object or value.
Since 1.0.0<br>
Group: object<br>
Author/Maintainer: aMarCruz<br>
### Example
```ts
import deepClone from '@jsbits/deep-clone'
let obj = {
foo: 1,
bar: 'bar',
baz: { date: new Date() },
}
let clone = deepClone(obj)
console.log('Success?',
clone.baz.date instanceof Date && clone.baz.date !== obj.baz.date)
// ⇒ true
/*
Using Symbol() as property name and the additional parameter
to clone the non-enumerable property "abc".
*/
const baz = Symbol()
obj = {
foo: 'Foo',
arr: [{ bar: 'Bar' }],
[baz]: 'Baz',
}
Object.defineProperty(obj, 'abc', {
value: 'xyz',
enumerable: false,
})
clone = deepClone(obj, true)
console.log(JSON.stringify(clone)) // ⇒ '{"foo":"Foo","arr":[{"bar":"Bar"}]}'
console.log(clone[baz]) // ⇒ 'Baz'
console.log(clone.abc) // ⇒ 'xyz'
console.dir(Object.getOwnPropertyDescriptor(clone, 'abc'))
// ⇒ { value: 'xyz',
// writable: false,
// enumerable: false,
// configurable: false }
```
### About getter and setters
Cloning of getters and setters work as expected, they are duplicated _by reference_. However, there' cases where cloning does not work.
Observe this fragment:
```ts
const createObj = function () {
let _foo = 'bar' // in closure
return Object.defineProperty({}, 'foo', {
get () { return _foo },
set (value) { _foo = value },
enumerable: true,
})
}
// This creates an object with a property `foo` with accessors that use the var `_foo` of its closure.
const obj = createObj()
// This will clone the object and the property `foo` with its accessors.
const clone = deepClone(obj)
// Looks like this works...
console.log(clone !== obj) // ⇒ true
console.log(clone.foo) // ⇒ 'bar'
clone.foo = 'BAZ'
console.log(clone.foo) // ⇒ 'BAZ'
console.log(obj.foo) // ⇒ 'BAZ' ...ups!
```
This is obvious if you look at the code of deepClone, getters and setters are copied but its closure is the same as the original object.
To date, I haven't found any way to solve this issue ...anyone?
A workaround is to keep the "hidden" variable in the object.
In this case, we move `_foo` to inside the object:
```ts
const createObj = function () {
// _foo as property
return Object.defineProperties({}, {
_foo: {
value: 'bar',
writable: true, // writable, but no enumerable
},
foo: {
get () { return this._foo },
set (value) { this._foo = value },
enumerable: true,
},
})
}
const obj = createObj()
const clone = deepClone(obj)
// Now this works
console.log(clone !== obj) // ⇒ true
console.log(clone.foo) // ⇒ 'bar'
clone.foo = 'BAZ'
console.log(clone.foo) // ⇒ 'BAZ'
console.log(obj.foo) // ⇒ 'bar' :)
// and...
console.log(JSON.stringify(obj)) // ⇒ '{"foo":"bar"}' +1
```
## Imports
All the [JSBits][jsbits-url] functions works in _strict mode_ and are compatible with:
- ES5 browsers, through the [jQuery](https://jquery.com/) `$.jsbits` object or the global `jsbits`.
- ESM Bundlers, like [webpack](http://webpack.github.io/) and [Rollup](https://rollupjs.org/).
- [ES modules](http://2ality.com/2014/09/es6-modules-final.html) for modern browsers or NodeJS with the `--experimental-modules` flag.
- [CommonJS](https://nodejs.org/docs/latest/api/modules.html#modules_modules) modules of NodeJS, jspm, and others.
- [Babel](https://babeljs.io/) and [TypeScript](www.typescriptlang.org/), through ES Module Interop.
Please see the [Distribution Formats][jsbits-formats] in the JSBits README to know about all the variants.
## Known Issues
This types are copied by reference:
- Function
- AsyncFunction
- Getters and Setters
- GeneratorFunction
- Iterators
- SharedArrayBuffer (ES2017, has a shered data block)
- Atomics object (ES2017)
- JSON object
- Math object
- WeakMap
- WeakSet
- XMLHttpRequest
`arguments` object is cloned as an object without prototype.
Untested types:
- Workers
- WebAssembly
The [Intl](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl) object and other classes are cloned as generic Objects.
## Support my Work
I'm a full-stack developer with more than 20 year of experience and I try to share most of my work for free and help others, but this takes a significant amount of time and effort so, if you like my work, please consider...
[<img src="https://amarcruz.github.io/images/kofi_blue.png" height="36" title="Support Me on Ko-fi" />][kofi-url]
Of course, feedback, PRs, and stars are also welcome 🙃
Thanks for your support!
## License
The [MIT](LICENSE) License.
© 2018-2019 Alberto Martínez – Readme powered by [jscc](https://github.com/aMarCruz/jscc) and [jsdoc-to-markdown](https://github.com/75lb/jsdoc-to-markdown)
[license-badge]: https://img.shields.io/badge/license-MIT-blue.svg?style=flat
[npm-badge]: https://img.shields.io/npm/v/@jsbits/deep-clone.svg
[npm-url]: https://www.npmjs.com/package/@jsbits/deep-clone
[appveyor-badge]: https://img.shields.io/appveyor/ci/aMarCruz/jsbits/master.svg?label=appveyor
[appveyor-url]: https://ci.appveyor.com/project/aMarCruz/jsbits/branch/master
[travis-badge]: https://img.shields.io/travis/ProJSLib/jsbits/master.svg?label=travis
[travis-url]: https://travis-ci.org/ProJSLib/jsbits
[codecov-badge]: https://img.shields.io/codecov/c/github/ProJSLib/jsbits/master.svg
[codecov-url]: https://codecov.io/gh/ProJSLib/jsbits/branch/master
[codacy-badge]: https://img.shields.io/codacy/grade/b9374fca91d64b75aafac26682df8fd0/master.svg
[codacy-url]: https://www.codacy.com/app/ProJSLib/jsbits?utm_source=github.com&utm_medium=referral&utm_content=ProJSLib/jsbits&utm_campaign=Badge_Grade
[climate-badge]: https://img.shields.io/codeclimate/maintainability/ProJSLib/jsbits.svg
[climate-url]: https://codeclimate.com/github/ProJSLib/jsbits/maintainability
[size-badge]: https://img.shields.io/bundlephobia/min/@jsbits/deep-clone.svg
[size-url]: https://bundlephobia.com/result?p=@jsbits/deep-clone
[jsbits-url]: https://github.com/ProJSLib/jsbits
[jsbits-formats]: https://github.com/ProJSLib/jsbits#distribution-formats
[kofi-url]: https://ko-fi.com/C0C7LF7I