davidmarkclements / rfdc

Really Fast Deep Clone
MIT License
643 stars 25 forks source link

✨ Add `constructorHandlers` option #40

Closed alecgibson closed 4 months ago

alecgibson commented 4 months ago

The motivation of this change is to allow passing custom handlers for particular classes. For example, ObjectId.

These can be passed using the new constructorHandlers option:

const clone = rfdc({
  constructorHandlers: [
    [ObjectId, (o) => new ObjectId(o)],
  ],
})

Similarly, RegExp support can be added manually:

const clone = rfdc({
  constructorHandlers: [
    [RegExp, (o) => new RegExp(o)],
  ],
})

Internally, the special handlers for Date, Map, and Set are moved to use this mechanism to keep code tidy.

Limitations

Note that - for performance - this is backed under the hood by a Map with the classes as keys, which gives constant-time lookup (compared to eg iterating over an array of handlers). A limitation that this introduces is that subclasses would not be matched, and would need their own handlers, since we don't look up the prototype chain.

Performance

Benchmarks before:

benchRfdc*100: 206.839ms
benchRfdcProto*100: 206.776ms
benchRfdcCircles*100: 231.711ms
benchRfdcCirclesProto*100: 229.874ms

Benchmarks after:

benchRfdc*100: 221.126ms
benchRfdcProto*100: 239.467ms
benchRfdcCircles*100: 241.456ms
benchRfdcCirclesProto*100: 257.926ms
mcollina commented 4 months ago

Can you document this in the README?

mcollina commented 4 months ago

Can you rebase your PR? I've fixed CI.