mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-27 06:43:19 +08:00
Each evaluation of a function-local class creates its own private names, while one compiled class serves every evaluation. A JavaScript class's prototype methods are closures per evaluation whose `this` is the call-site value, so a method taken from one evaluation could be called with another evaluation's instance (`a.next.call(b)`, a prototype read, Reflect.get with a receiver). Those calls read and wrote the other instance's fields, and `#x in this` answered true. Reads and writes of `this.#x` inside the closures also went through the property bag under the key "#x" instead of the native field. Private fields on a checked `this` now go through a brand check: the receiver must be an instance of the class or a subclass whose class environment is the closure's own evaluation, which the class's own binding names. A matching receiver reads and writes the native field; any other receiver throws Node's TypeError for the read or the write (after the assigned value evaluates), and `#x in this` answers false. Method values and accessors taken from typed instances of these classes resolve to the instance's own evaluation's prototype functions instead of a native thunk shared by every evaluation. The classes this applies to are narrowed to the shapes the check covers: JavaScript class declarations whose private members are instance fields used only on `this`. Private methods and accessors, class expressions, and TypeScript classes keep the "function-local classes with private brands" refusal.
23 lines
500 B
JavaScript
23 lines
500 B
JavaScript
// A method of a function-local class that never reads `this` keeps working
|
|
// when it is taken off an instance and called detached or with another
|
|
// receiver.
|
|
'use strict';
|
|
|
|
function make(label) {
|
|
class Box {
|
|
one() {
|
|
return 1;
|
|
}
|
|
|
|
named() {
|
|
return `box ${label}`;
|
|
}
|
|
}
|
|
return new Box();
|
|
}
|
|
|
|
const box = make('a');
|
|
const one = box.one;
|
|
console.log(one(), box.one.call(null), box.one.call({}), box.named());
|
|
console.log([box, make('b')].map((b) => b.one()).join(','));
|