From 2b22e4230a224a191b26b2d97346a0569a4a71b6 Mon Sep 17 00:00:00 2001 From: Tony Quetano Date: Mon, 26 Sep 2022 10:16:42 -0400 Subject: [PATCH] update documentation --- CHANGELOG.md | 4 +++- README.md | 68 ++++++++++++++++++++++++++++++---------------------- src/index.ts | 12 ++-------- 3 files changed, 44 insertions(+), 40 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fc26d65..af25919 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,9 @@ - `copy` is now a named export instead of a default export - `copy.strict` is no longer available; it is now available as the explicit `copyStrict` named import -- `FastCopy` TS namespace has been removed in favor of explicit type imports +- Options have been removed + - `isStrict` has been replaced with importing `copyStrict` + - `realm` has been removed, as `instanceof` is no longer used internally ## 2.1.7 diff --git a/README.md b/README.md index 25a1db1..60b3db9 100644 --- a/README.md +++ b/README.md @@ -11,10 +11,13 @@ A [blazing fast](#benchmarks) deep object copier - [fast-copy](#fast-copy) - [Table of contents](#table-of-contents) - [Usage](#usage) - - [Options](#options) - - [isStrict](#isstrict) - - [realm](#realm) + - [API](#api) + - [`copy`](#copy) + - [`copyStrict`](#copystrict) - [Types supported](#types-supported) + - [Aspects of copying](#aspects-of-copying) + - [Error references are copied over (instead of creating a new `*Error` object)](#error-references-are-copied-over-instead-of-creating-a-new-error-object) + - [The constructor of the original object is used, instead of using known globals.](#the-constructor-of-the-original-object-is-used-instead-of-using-known-globals) - [Benchmarks](#benchmarks) - [Simple objects](#simple-objects) - [Complex objects](#complex-objects) @@ -43,44 +46,39 @@ console.log(copiedObject === object); // false console.log(deepEqual(copiedObject, object)); // true ``` -## Options +## API -#### isStrict +### `copy` -Starting in `2.0.0`, you can use the `isStrict` option to copy the object based on strict standards, meaning: +Deeply copy the object passed. -- Properties retain their original property descriptor -- Non-enumerable properties are copied -- Non-standard properties (e.g., keys on an `Array` object) are copied - -This is significantly slower, so you should only use this if your use-case requires it. +```ts +import { copy } from 'fast-copy'; -```javascript -console.log(copy(object, { isStrict: true })); +const copied = copy({ foo: 'bar' }); ``` -**NOTE**: This option is also aliased as `copyStrict`. +### `copyStrict` -```javascript -import { copyStrict } from 'fast-copy'; +Deeply copy the object passed, but with additional strictness when replicating the original object: -console.log(copyStrict(object)); -``` +- Properties retain their original property descriptor +- Non-enumerable keys are copied +- Non-standard properties (e.g., keys on an `Array` object) are copied -#### realm +```ts +import { copyStrict } from 'fast-copy'; -Under the hood, `fast-copy` uses `instanceof` to determine object types, which can cause false negatives when used in combination with `iframe`-based objects. To handle this edge case, you can pass the `realm` in options, which identifies which realm the object comes from and will use that realm to drive both comparisons and constructors for the copies. +const object = { foo: 'bar' }; +object.nonEnumerable = Object.defineProperty(object, 'bar', { + enumerable: false, + value: 'baz', +}); -```html - +const copied = copy(object); ``` -```javascript -const iframe = document.querySelector('iframe'); -const arr = iframe.contentWindow.arr; - -console.log(copy(arr, { realm: iframe.contentWindow })); // ['foo', 'bar'] -``` +**NOTE**: This method is significantly slower than [`copy`](#copy), so it is recommended to only use this when you have specific use-cases that require it. ## Types supported @@ -124,7 +122,19 @@ The following object types are copied directly, as they are either primitives, c - `WeakMap` - `WeakSet` -Circular objects are supported out of the box as well. By default a cache based on `WeakSet` is used, but if `WeakSet` is not available then a standard `Object` fallback is used. The benchmarks quoted below are based on use of `WeakSet`. +Circular objects are supported out of the box. By default, a cache based on `WeakSet` is used, but if `WeakSet` is not available then a fallback is used. The benchmarks quoted below are based on use of `WeakSet`. + +## Aspects of copying + +Inherently, what is considered a valid copy is subjective because of different requirements and use-cases. For this library, some decisions were explicitly made. + +### Error references are copied over (instead of creating a new `*Error` object) + +While it would be relatively trivial to copy over the message and stack to a new object of the same `Error` subclass, it is a common practice to "override" the message or stack, and copies would not retain this mutation. As such, the original reference is copied. + +### The constructor of the original object is used, instead of using known globals. + +Starting in ES2015, native globals can be subclassed like any custom class. When copying, we explicitly reuse the constructor of the original object. However, the expectation is that these subclasses would have the same constructur signature as their native base class. This is a common community practice, however because there is the possibility of inaccuracy if the contract differs, it should be noted. ## Benchmarks diff --git a/src/index.ts b/src/index.ts index 75ad05a..df3b151 100644 --- a/src/index.ts +++ b/src/index.ts @@ -83,27 +83,19 @@ function performCopy( // maps if (objectClass === '[object Map]') { - const clone = new Constructor(); + const clone = new Constructor(value.entries()); cache.set(value, clone); - value.forEach((value: any, key: any) => { - clone.set(key, handleCopy(value, cache)); - }); - return clone; } // sets if (objectClass === '[object Set]') { - const clone = new Constructor(); + const clone = new Constructor(value.values()); cache.set(value, clone); - value.forEach((value: any) => { - clone.add(handleCopy(value, cache)); - }); - return clone; }