Function prototypeChainOfObject

  • get the prototype chain of an object, with optional slicing options.

    Parameters

    • obj: any

      the object whose prototype chain is to be found.

    • config: PrototypeChainOfObjectConfig = {}

      optional configuration for slicing the full prototype chain.

    Returns object[]

    the sliced prototype chain of the given object.

    import { assertEquals, assertThrows } from "jsr:@std/assert"

    // aliasing our functions for brevity
    const
    fn = prototypeChainOfObject,
    eq = assertEquals

    class A extends Array { }
    class B extends A { }
    class C extends B { }
    class D extends C { }

    const
    a = new A(0),
    b = new B(0),
    c = new C(0),
    d = new D(0)

    eq(fn(d), [D.prototype, C.prototype, B.prototype, A.prototype, Array.prototype, Object.prototype, null])
    eq(fn(b), [B.prototype, A.prototype, Array.prototype, Object.prototype, null])

    // slicing the prototype chain, starting from index 2 till the end
    eq(fn(d, { start: 2 }), [B.prototype, A.prototype, Array.prototype, Object.prototype, null])

    // slicing using a negative index
    eq(fn(d, { start: -2 }), [Object.prototype, null])

    // slicing using an object
    eq(fn(d, { start: B.prototype }), [B.prototype, A.prototype, Array.prototype, Object.prototype, null])

    // when the slicing object is not found, the start index will be assumed to be `0` (default value)
    eq(fn(d, { start: Set.prototype }), [D.prototype, C.prototype, B.prototype, A.prototype, Array.prototype, Object.prototype, null])

    // slicing between the `start` index (inclusive) and the end index
    eq(fn(d, { start: 2, end: 6 }), [B.prototype, A.prototype, Array.prototype, Object.prototype])
    eq(fn(d, { start: 2, end: -1 }), [B.prototype, A.prototype, Array.prototype, Object.prototype])
    eq(fn(d, { start: 2, end: null }), [B.prototype, A.prototype, Array.prototype, Object.prototype])

    // if the end index is not found, the slicing will occur till the end
    eq(fn(d, { end: Set.prototype}), [D.prototype, C.prototype, B.prototype, A.prototype, Array.prototype, Object.prototype, null])

    // slicing using a `delta` argument will let you define how many elements you wish to:
    // - traverse forward from the `start` index
    // - traverse backwards from the `end` index
    eq(fn(d, { start: 2, delta: 3 }), [B.prototype, A.prototype, Array.prototype])
    eq(fn(d, { end: -2, delta: 2 }), [A.prototype, Array.prototype])
    eq(fn(d, { end: null, delta: 4 }), [B.prototype, A.prototype, Array.prototype, Object.prototype])

    eq(fn(d, { start: 1, delta: 1 }), fn(c, { start: 0, delta: 1 }))
    eq(fn(c, { start: 1, delta: 1 }), fn(b, { start: 0, delta: 1 }))
    eq(fn(b, { start: 1, delta: 1 }), fn(a, { start: 0, delta: 1 }))
    eq(fn(a, { start: 1, delta: 1 }), fn([], { start: 0, delta: 1 }))
    eq(fn([], { start: 1, delta: 1 }), fn({}, { start: 0, delta: 1 }))

    // you may also traverse through the inheritance chain of a class, but it will be a good idea to set your `end` point to `Function.prototype`,
    // since all class objects are effectively functions (i.e. their common ancestral prototype if `Function.prototype`).
    eq(fn(D, { start: 0, end: Function.prototype }), [C, B, A, Array])
    eq(fn(D), [C, B, A, Array, Function.prototype, Object.prototype, null])
    eq(fn(class {}), [Function.prototype, Object.prototype, null])
    eq(fn(class extends Object {}), [Object, Function.prototype, Object.prototype, null])
    eq(fn(class extends Map {}), [Map, Function.prototype, Object.prototype, null])

    // you cannot acquire the prototype chain of the `null` object
    assertThrows(() => { fn(null) })