A structural deep clone with extras. copy(value) preserves the
prototype chain (so class instances stay instanceof), copies
non-enumerable and symbol keys with their full descriptor attributes,
handles cycles, and re-applies frozen / sealed / non-extensible state.
Own accessor properties are snapshotted: the getter is invoked once
inside untrack and the result stored as plain data, so the
copy holds a value, not a live recomputing view. Getters defined on
the prototype — class get accessors — are not own properties, so
they stay live through the preserved prototype chain. Built-ins listed
in the mutation blacklist (Date, RegExp, DOM elements, …) pass
through by reference.
copy is what mutable uses when asked to clone, and
what the reconcilers (merge,
replace, reset) run on source so
merging never mutates it.
| name | type | description |
|---|---|---|
value |
T |
value to deep-copy; non-objects pass through |
Returns: a deep copy of value (same type T). Non-object inputs
and blacklisted built-ins are returned unchanged.
The clone keeps the prototype — it stays instanceof Point and the
magnitude getter still computes from the copied x and y — and
preserves a self-referential cycle.
import { copy } from 'pota/store'
import { render } from 'pota'
class Point {
x
y
constructor(x, y) {
this.x = x
this.y = y
}
get magnitude() {
return Math.hypot(this.x, this.y)
}
}
const a = new Point(3, 4)
const b = copy(a)
const cyclic = { name: 'self', me: null }
cyclic.me = cyclic
const c = copy(cyclic)
render(
<pre>
{JSON.stringify(
{
instanceOf: b instanceof Point,
values: [b.x, b.y, b.magnitude],
sameRef: a === b,
cyclePreserved: c.me === c,
},
null,
2,
)}
</pre>,
)
A getter declared in an object literal is an own accessor, so copy
stores its result at copy time — the copy's total stops recomputing,
while the original's getter stays live.
import { copy } from 'pota/store'
import { render } from 'pota'
const cart = {
price: 10,
amount: 2,
get total() {
return this.price * this.amount
},
}
const snap = copy(cart)
cart.price = 100
snap.price = 100
render(
<pre>
{JSON.stringify(
{ original: cart.total, copied: snap.total },
null,
2,
)}
</pre>,
)