123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181 |
- # rfdc
- Really Fast Deep Clone
- [](https://travis-ci.org/davidmarkclements/rfdc)
- [](https://codecov.io/gh/davidmarkclements/rfdc)
- [](http://standardjs.com/)
- ## Usage
- ```js
- const clone = require('rfdc')()
- clone({a: 1, b: {c: 2}}) // => {a: 1, b: {c: 2}}
- ```
- ## API
- ### `require('rfdc')(opts = { proto: false, circles: false, constructorHandlers: [] }) => clone(obj) => obj2`
- #### `proto` option
- Copy prototype properties as well as own properties into the new object.
- It's marginally faster to allow enumerable properties on the prototype
- to be copied into the cloned object (not onto it's prototype, directly onto the object).
- To explain by way of code:
- ```js
- require('rfdc')({ proto: false })(Object.create({a: 1})) // => {}
- require('rfdc')({ proto: true })(Object.create({a: 1})) // => {a: 1}
- ```
- Setting `proto` to `true` will provide an additional 2% performance boost.
- #### `circles` option
- Keeping track of circular references will slow down performance with an
- additional 25% overhead. Even if an object doesn't have any circular references,
- the tracking overhead is the cost. By default if an object with a circular
- reference is passed to `rfdc`, it will throw (similar to how `JSON.stringify` \
- would throw).
- Use the `circles` option to detect and preserve circular references in the
- object. If performance is important, try removing the circular reference from
- the object (set to `undefined`) and then add it back manually after cloning
- instead of using this option.
- #### `constructorHandlers` option
- Sometimes consumers may want to add custom clone behaviour for particular classes
- (for example `RegExp` or `ObjectId`, which aren't supported out-of-the-box).
- This can be done by passing `constructorHandlers`, which takes an array of tuples,
- where the first item is the class to match, and the second item is a function that
- takes the input and returns a cloned output:
- ```js
- const clone = require('rfdc')({
- constructorHandlers: [
- [RegExp, (o) => new RegExp(o)],
- ]
- })
- clone({r: /foo/}) // => {r: /foo/}
- ```
- **NOTE**: For performance reasons, the handlers will only match an instance of the
- *exact* class (not a subclass). Subclasses will need to be added separately if they
- also need special clone behaviour.
- ### `default` import
- It is also possible to directly import the clone function with all options set
- to their default:
- ```js
- const clone = require("rfdc/default")
- clone({a: 1, b: {c: 2}}) // => {a: 1, b: {c: 2}}
- ```
- ### Types
- `rfdc` clones all JSON types:
- * `Object`
- * `Array`
- * `Number`
- * `String`
- * `null`
- With additional support for:
- * `Date` (copied)
- * `undefined` (copied)
- * `Buffer` (copied)
- * `TypedArray` (copied)
- * `Map` (copied)
- * `Set` (copied)
- * `Function` (referenced)
- * `AsyncFunction` (referenced)
- * `GeneratorFunction` (referenced)
- * `arguments` (copied to a normal object)
- All other types have output values that match the output
- of `JSON.parse(JSON.stringify(o))`.
- For instance:
- ```js
- const rfdc = require('rfdc')()
- const err = Error()
- err.code = 1
- JSON.parse(JSON.stringify(e)) // {code: 1}
- rfdc(e) // {code: 1}
- JSON.parse(JSON.stringify({rx: /foo/})) // {rx: {}}
- rfdc({rx: /foo/}) // {rx: {}}
- ```
- ## Benchmarks
- ```sh
- npm run bench
- ```
- ```
- benchDeepCopy*100: 671.675ms
- benchLodashCloneDeep*100: 1.574s
- benchCloneDeep*100: 936.792ms
- benchFastCopy*100: 822.668ms
- benchFastestJsonCopy*100: 363.898ms // See note below
- benchPlainObjectClone*100: 556.635ms
- benchNanoCopy*100: 770.234ms
- benchRamdaClone*100: 2.695s
- benchJsonParseJsonStringify*100: 2.290s // JSON.parse(JSON.stringify(obj))
- benchRfdc*100: 412.818ms
- benchRfdcProto*100: 424.076ms
- benchRfdcCircles*100: 443.357ms
- benchRfdcCirclesProto*100: 465.053ms
- ```
- It is true that [fastest-json-copy](https://www.npmjs.com/package/fastest-json-copy) may be faster, BUT it has such huge limitations that it is rarely useful. For example, it treats things like `Date` and `Map` instances the same as empty `{}`. It can't handle circular references. [plain-object-clone](https://www.npmjs.com/package/plain-object-clone) is also really limited in capability.
- ## Tests
- ```sh
- npm test
- ```
- ```
- 169 passing (342.514ms)
- ```
- ### Coverage
- ```sh
- npm run cov
- ```
- ```
- ----------|----------|----------|----------|----------|-------------------|
- File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
- ----------|----------|----------|----------|----------|-------------------|
- All files | 100 | 100 | 100 | 100 | |
- index.js | 100 | 100 | 100 | 100 | |
- ----------|----------|----------|----------|----------|-------------------|
- ```
- ### `__proto__` own property copying
- `rfdc` works the same way as `Object.assign` when it comes to copying `['__proto__']` (e.g. when
- an object has an own property key called '__proto__'). It results in the target object
- prototype object being set per the value of the `['__proto__']` own property.
- For detailed write-up on how a way to handle this security-wise see https://www.fastify.io/docs/latest/Guides/Prototype-Poisoning/.
- ## License
- MIT
|