utility functions for creating other general purpose functions that can bind their passed function's functionality to some specific object.

those are certainly a lot of words thrown in the air with no clarity as to what am I even saying. just as they say, a code block example is worth a thousand assembly instructions. here's the gist of it:

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

const bind_pushing_to = bindMethodFactory(Array.prototype.push) // equivalent to `bindMethodFactoryByName(Array.prototype, "push")`
const bind_seek_to = bindMethodFactory(Array.prototype.at, -1) // equivalent to `bindMethodFactoryByName(Array.prototype, "at", -1)`
const bind_splicing_to = bindMethodFactoryByName(Array.prototype, "splice") // equivalent to `bindMethodFactory(Array.prototype.splice)`
const bind_clear_to = bindMethodFactoryByName(Array.prototype, "splice", 0) // equivalent to `bindMethodFactory(Array.prototype.splice, 0)`

const my_array = [1, 2, 3, 4, 5, 6]
const push_my_array = bind_pushing_to(my_array)
const seek_my_array = bind_seek_to(my_array)
const splice_my_array = bind_splicing_to(my_array)
const clear_my_array = bind_clear_to(my_array) as (deleteCount?: number, ...items: number[]) => number[]

push_my_array(7, 8, 9)
assertEquals(my_array, [1, 2, 3, 4, 5, 6, 7, 8, 9])
assertEquals(seek_my_array(), 9)
splice_my_array(4, 3)
assertEquals(my_array, [1, 2, 3, 4, 8, 9])
clear_my_array()
assertEquals(my_array, [])

you may have some amateur level questions about why? would anyone want to do that. here is why:

  • calling a method via property access is slower. so when you call an array arr's push or pop methods a million times, having a bound function for that specific purpose will be quicker by about x1.3 times:
import { assertLess } from "jsr:@std/assert"
import { timeIt } from "./timeman.ts"

// WARNING: JIT is pretty smart, and the test consistiently fails for `i` and `j` > `10_000_000`,
// this is despite my efforts to make it difficult for the JIT to optimize the slow method, by computing some modulo and only popping when it is zero.
// thus to be safe, I tuned `i` and `j` down to `100_000` iterations

let i = 100_000, j = 100_000, sum1 = 0, sum2 = 0
const
arr1 = Array(777).fill(0).map(Math.random),
arr2 = Array(777).fill(0).map(Math.random)

// slower way:
const t1 = timeIt(() => {
while(i--) {
const new_length = arr1.push(Math.random())
sum1 += ((new_length + i) % 3 === 0 ? arr1.pop()! : arr1.at(-1)!)
}
})

// faster way (allegedly):
const
push_arr2 = Array.prototype.push.bind(arr2),
pop_arr2 = Array.prototype.pop.bind(arr2),
seek_arr2 = Array.prototype.at.bind(arr2, -1)
const t2 = timeIt(() => {
while(j--) {
const new_length = push_arr2(Math.random())
sum2 += ((new_length + i) % 3 === 0 ? pop_arr2()! : seek_arr2()!)
}
})

// assertLess(t2, t1) // TODO: RIP, performance gains have diminished in deno. curse you V8 JIT.
// I still do think that in non-micro-benchmarks and real life applications (where there are a variety of objects and structures),
// there still is a bit of performance gain. and lets not forget the benefit of minifiability.

next, you may be wondering why not destructure the method or assign it to a variable?

  • this cannot be generally done for prototype-bound methods, because it needs the context of who is the caller (and therefor the this of interest). all builtin javascript class methods are prototype-bound. meaning that for every instance of a builtin class no new functions are specifically created for that instance, and instead, the instance holds a reference to the class's prototype object's method, but applies itself as the this when called.
import { assertEquals, assertThrows } from "jsr:@std/assert"

const arr = [1, 2, 3, 4, 5, 6]

// prototype-bound methods need to be called via property access, otherwise they will loose their `this` context when uncoupled from their parent object
const { push, pop } = arr
assertThrows(() => push(7, 8, 9)) // `TypeError: Cannot convert undefined or null to object`
assertThrows(() => pop()) // `TypeError: Cannot convert undefined or null to object`

const
push2 = arr.push,
pop2 = arr.pop
assertThrows(() => push2(7, 8, 9)) // `TypeError: Cannot convert undefined or null to object`
assertThrows(() => pop2()) // `TypeError: Cannot convert undefined or null to object`

// but you can do the binding yourself too to make it work
const push3 = arr.push.bind(arr) // equivalent to `Array.prototype.push.bind(arr)`
const pop3 = arr.pop.bind(arr) // equivalent to `Array.prototype.pop.bind(arr)`
push3(7, 8, 9) // will work
pop3() // will work

// or use this submodule to do the same thing:
// import { bind_array_pop, bind_array_push } from "@oazmi/kitchensink/binder"
const push4 = bind_array_push(arr)
const pop4 = bind_array_pop(arr)
push4(7, 8, 9) // will work
pop4() // will work
  • finally, property accesses are not easily minifiable (although they do get compressed when gzipped). however, if you bind your method calls to a variable, then it will become minifiable, which is somewhat the primary motivation for this submodule.

with full automatic typing, you won't be compensating in any way. on the side note, it was figuring out the automatic typing that took me almost 16 hours just to write 3 lines of equivalent javascript code for the main 2 factory functions of this submodule. curse you typescript!

Type Aliases

BindableFunction

Functions

bindMethodFactory
bindMethodFactoryByName
bindMethodToSelf
bindMethodToSelfByName
bind_array_at
bind_array_concat
bind_array_copyWithin
bind_array_entries
bind_array_every
bind_array_fill
bind_array_filter
bind_array_find
bind_array_findIndex
bind_array_findLast
bind_array_findLastIndex
bind_array_flat
bind_array_flatMap
bind_array_forEach
bind_array_includes
bind_array_indexOf
bind_array_join
bind_array_keys
bind_array_lastIndexOf
bind_array_map
bind_array_pop
bind_array_push
bind_array_reduce
bind_array_reduceRight
bind_array_reverse
bind_array_shift
bind_array_slice
bind_array_some
bind_array_sort
bind_array_splice
bind_array_unshift
bind_array_toLocaleString
bind_array_toReversed
bind_array_toSorted
bind_array_toSpliced
bind_array_toString
bind_array_values
bind_array_with
bind_array_clear
bind_stack_seek
bind_set_add
bind_set_clear
bind_set_delete
bind_set_entries
bind_set_forEach
bind_set_has
bind_set_keys
bind_set_values
bind_map_clear
bind_map_delete
bind_map_entries
bind_map_forEach
bind_map_get
bind_map_has
bind_map_keys
bind_map_set
bind_map_values
bind_string_at
bind_string_charAt
bind_string_charCodeAt
bind_string_codePointAt
bind_string_startsWith
bind_string_endsWith