Skip to content

Latest commit

 

History

History
218 lines (156 loc) · 7.76 KB

obj_readme.md

File metadata and controls

218 lines (156 loc) · 7.76 KB

Overview

obj.mjs provides tools for manipulating JS objects in weird ways.

TOC

Usage

import * as o from 'https://cdn.jsdelivr.net/npm/@mitranim/[email protected]/obj.mjs'

API

function assign

Links: source; test/example.

Signature: (tar, src) => tar.

Similar to Object.assign. Differences:

  • Much faster.
  • Exactly two parameters, not variadic.
  • Sanity-checked:
    • Target must be a struct.
    • Source must be nil or a struct.
    • Throws on invalid inputs.

Similar to #patch but doesn't check for inherited and non-enumerable properties. Simpler, dumber, faster.

function patch

Links: source; test/example.

Signature: (tar, src) => tar.

Similar to Object.assign. Differences:

  • Much faster.
  • Takes only two args.
  • Sanity-checked:
    • Target must be a struct.
    • Source must be nil or a struct.
    • Throws on invalid inputs.
    • Does not override inherited properties.
    • Does not override own non-enumerable properties.

When overriding inherited and non-enumerable properties is desirable, use #assign.

class Struct

Links: source; test/example.

Superclass for classes representing a "struct" / "model" / "record". Also see #StructLax. Features:

  • Supports property declarations, with validation/transformation functions.

  • Can be instantiated or mutated from any struct (any dict-like object).

  • Assigns and checks all declared properties when instantiating via new. Ignores undeclared properties.

  • Assigns and checks all declared properties when mutating via .mut with a non-nil argument. Ignores undeclared properties.

  • When mutating an existing struct via .mut, supports calling method .mut on existing property values which implement #the. This allows deep/recursive mutation.

  • Uses regular JS properties. Does not use getters/setters, proxies, private properties, non-enumerable properties, symbols, or anything else "strange". Declared properties are simply assigned via =.

Performance characteristics:

  • The cost of instantiating or mutating depends only on declared properties, not on provided properties.

  • When the number of declared properties is similar to the number of provided properties, this tends to be slightly slower than Object.assign or #assign.

  • When the number of declared properties is significantly smaller than the number of provided properties, this tends to be faster than the aforementioned assignment functions.

import * as l from 'https://cdn.jsdelivr.net/npm/@mitranim/[email protected]/lang.mjs'
import * as o from 'https://cdn.jsdelivr.net/npm/@mitranim/[email protected]/obj.mjs'

class Person extends o.Struct {
  static spec = {
    id: l.reqFin,
    name: l.reqStr,
  }
}

// Fails the type check.
new Person({id: 10})
/* Uncaught TypeError: invalid property "name" */

// Fails the type check.
new Person({name: `Mira`})
/* Uncaught TypeError: invalid property "id" */

// Satisfies the type check.
new Person({id: 10, name: `Mira`})
/* Person { id: 10, name: "Mira" } */

// Ignores undeclared properties.
new Person({id: 10, name: `Mira`, slug: `mira`, gender: `female`})
/* Person { id: 10, name: "Mira" } */

class StructLax

Links: source; test/example.

Superclass for classes representing a "struct" / "model" / "record". Subclass of #Struct with added support for undeclared properties.

Differences from #Struct:

  • When instantiating via new or mutating via .mut, in addition to assigning and checking all declared properties, this also copies any undeclared properties present in the source data.

    • Behaves similarly to #patch, and differently from Object.assign or #assign. Avoids accidentally shadowing inherited or non-enumerable properties.

    • Just like with declared properties, copying undeclared properties supports deep/recursive mutation by calling .mut on any existing property values that implement #the.

  • Measurably worse performance.

import * as l from 'https://cdn.jsdelivr.net/npm/@mitranim/[email protected]/lang.mjs'
import * as o from 'https://cdn.jsdelivr.net/npm/@mitranim/[email protected]/obj.mjs'

class Person extends o.StructLax {
  static spec = {
    id: l.reqFin,
    name: l.reqStr,
  }
}

// Fails the type check.
new Person({id: 10})
/* Uncaught TypeError: invalid property "name" */

// Fails the type check.
new Person({name: `Mira`})
/* Uncaught TypeError: invalid property "id" */

// Satisfies the type check.
new Person({id: 10, name: `Mira`})
/* Person { id: 10, name: "Mira" } */

// Assigns undeclared properties in addition to declared properties.
new Person({id: 10, name: `Mira`, slug: `mira`, gender: `female`})
/* Person { id: 10, name: "Mira", slug: "mira", gender: "female" } */

function memGet

Links: source; test/example.

Takes a class and hacks its prototype, converting all non-inherited getters to lazy/memoizing versions of themselves that only execute once. The resulting value replaces the getter. Inherited getters are unaffected.

import * as o from 'https://cdn.jsdelivr.net/npm/@mitranim/[email protected]/obj.mjs'

class Bucket {
  static {o.memGet(this)}
  get one() {return new o.StructLax()}
  get two() {return new o.StructLax()}
}

const ref = new Bucket()
// Bucket {}

ref.one.three = 30
ref
// Bucket { one: Struct { three: 30 } }

ref.two.four = 40
ref
// Bucket { one: Struct { three: 30 }, two: Struct { four: 40 } }

Undocumented

The following APIs are exported but undocumented. Check obj.mjs.