tgoop.com/HowProgrammingWorks/1822
Last Update:
- ΠΈΠΌΠΌΡΡΠ°Π±Π΅Π»ΡΠ½ΡΠ΅ ΠΌ ΠΌΡΡΠ°Π±Π΅Π»ΡΠ½ΡΠ΅ Π·Π°ΠΏΠΈΡΠΈ (ΠΎΠ±ΡΠ΅ΠΊΡΡ) Ρ ΠΎΠΏΡΠΈΠΌΠΈΠ·Π°ΡΠΈΠ΅ΠΉ ΡΠΎΡΠΌΡ
- ΠΏΠΎΠ»Π½ΠΎΠ΅ ΠΊΠ»ΠΎΠ½ΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅, ΠΌΠΎΠ΄ΠΈΡΠΈΠΊΠ°ΡΠΈΡ, ΠΊΠ»ΠΎΠ½ΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅ Ρ ΠΏΡΠΎΡΠΎΡΠΈΠΏΠ½ΡΠΌ Π½Π°ΡΠ»Π΅Π΄ΠΎΠ²Π°Π½ΠΈΠ΅ΠΌ
- ΠΏΡΠΎΠ²Π΅ΡΠΊΠ° ΡΠΈΠΏΠΎΠ² Π² ΡΠ°Π½ΡΠ°ΠΉΠΌΠ΅, Π΄Π΅ΡΠΎΠ»ΡΠ½ΡΠ΅ Π·Π½Π°ΡΠ΅Π½ΠΈΡ ΠΏΠΎΠ»Π΅ΠΉ
- ΠΈΠΌΠΌΡΡΠ°Π±Π΅Π»ΡΠ½ΡΠ΅ Π·Π°ΠΏΠΈΡΠΈ Ρ ΡΠΊΠΎΠ½ΠΎΠΌΠΈΠ΅ΠΉ ΠΏΠ°ΠΌΡΡΠΈ Π·Π° ΡΡΠ΅Ρ ΡΠ΅ΠΏΠΎΡΠ΅ΠΊ ΠΏΡΠΎΡΠΎΡΠΈΠΏΠΎΠ²
- ΡΡΠΊΠΎΡΠ΅Π½ΠΈΠ΅ ΠΏΠΎΠΈΡΠΊΠ° ΠΏΠΎΠ»Π΅ΠΉ ΠΈ ΠΎΠΏΡΠΈΠΌΠΈΠ·Π°ΡΠΈΠΈ Π΄Π»Ρ V8, Π΄Π°ΠΆΠ΅ Π² ΡΡΠ°Π²Π½Π΅Π½ΠΈΠΈ Ρ Π»ΠΈΡΠ΅ΡΠ°Π»ΠΎΠΌ ΠΎΠ±ΡΠ΅ΠΊΡΠ°
Π’ΡΡ Π΅ΡΠ΅ Π½ΡΠΆΠ½ΠΎ ΠΌΠ½ΠΎΠ³ΠΎΠ΅ ΠΎΠΏΡΠΈΠΌΠΈΠ·ΠΈΡΠΎΠ²Π°ΡΡ, Π½ΠΎ ΠΏΠΎΠ»Π½ΠΎΠ΅ ΡΠ΅ΡΠ΅Π½ΠΈΠ΅ Π±ΡΠ΄Π΅Ρ ΡΡΡ: https://github.com/metarhia/metautil/pull/298
class Record {
static immutable(defaults) {
return Record.#build(defaults, false);
}
static mutable(defaults) {
return Record.#build(defaults, true);
}
static #build(defaults, isMutable) {
const fields = Object.keys(defaults);
const defaultValues = Object.create(null);
for (const key of fields) {
defaultValues[key] = defaults[key];
}
class Struct {
static fields = fields;
static defaults = defaultValues;
static mutable = isMutable;
static create(data = {}) {
const obj = Object.create(null);
for (const key of fields) {
const base = defaultValues[key];
const value = key in data ? data[key] : base;
if (!Record.#sameType(base, value)) {
const exp = Record.#typeof(base);
const act = Record.#typeof(value);
throw new TypeError(
`Invalid type for "${key}": expected ${exp}, got ${act}`,
);
}
obj[key] = value;
}
return isMutable ? Object.seal(obj) : Object.freeze(obj);
}
}
return Struct;
}
static #typeof(value) {
if (Array.isArray(value)) return 'array';
if (value === null) return 'null';
return typeof value;
}
static #sameType(a, b) {
if (Array.isArray(a)) return Array.isArray(b);
if (a === null) return b === null;
return typeof a === typeof b;
}
static #validate(instance, updates) {
for (const key of Object.keys(updates)) {
if (!Reflect.has(instance, key)) continue;
const current = instance[key];
const next = updates[key];
if (!Record.#sameType(current, next)) {
const exp = Record.#typeof(current);
const act = Record.#typeof(next);
throw new TypeError(
`Invalid type for "${key}": expected ${exp}, got ${act}`,
);
}
}
}
static update(instance, updates) {
if (Object.isFrozen(instance)) {
throw new Error('Cannot mutate immutable Record');
}
Record.#validate(instance, updates);
for (const key of Object.keys(updates)) {
if (Reflect.has(instance, key)) {
instance[key] = updates[key];
}
}
return instance;
}
static fork(instance, updates) {
Record.#validate(instance, updates);
const obj = Object.create(null);
for (const key of Object.keys(instance)) {
obj[key] = Reflect.has(updates, key) ? updates[key] : instance[key];
}
return Object.isFrozen(instance) ? Object.freeze(obj) : Object.seal(obj);
}
static branch(instance, updates) {
Record.#validate(instance, updates);
const obj = Object.create(instance);
for (const key of Object.keys(updates)) {
Reflect.defineProperty(obj, key, {
value: updates[key],
writable: true,
configurable: true,
enumerable: true,
});
}
return Object.isFrozen(instance) ? Object.freeze(obj) : Object.seal(obj);
}
}