Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 8
On programming with Hardened JavaScript
Erik Marks edited this page Aug 2, 2024
·
1 revision
Consider these two implementations of a counter capability:
// ClosureexportconstmakeCapability=()=>{letcounter=0;returnharden({inc(){counter+=1;returncounter;},});};harden(makeCapability);// ClassexportclassCapability{
#counter =0;constructor(){harden(this);}inc(){this.#counter +=1;returnthis.#counter;}}harden(Capability);Are they behaviorally equivalent? Despite appearances:
constcap1=makeCapability();constcap2=newCapability();console.log(cap1.inc()===cap2.inc());// trueconsole.log(cap1.inc()===cap2.inc());// truecap1.inc();console.log(cap1.inc()===cap2.inc());// falseThey in fact differ in at least two important respects:
- Methods that depend on private state can be usefully extracted from the closure, but not the class implementation.
const { inc } = makeCapability(); inc();works, butconst { inc } = new Capability(); inc();throws aTypeError.
- Class instances can be "hijacked" from a distance.
const instance = new Capability(); (new Capability()).inc.call(instance)affects an instance without access to any of its methods.- This is because
#-private fields are class-private, not instance-private, whereas the closure's private state is instance-private.
Agoric's documentation touches on these topics here. They have also explored the idea of using JavaScript classes as Exo definitions (ref).
Thanks to @gibson042 and @mhofman for elucidating these things.