Study Guide 2023+

javascript

Warning: These notes are partial, ongoing, incomplete, and may contain typos/inaccuracies. (They are kept factually accurate, time permitting.)

They are being united from many disparate notes created in the past and the layout/organization will gradually improve with time!

Please view them on a computer as they are not optimized for mobile (although you can still view them on Mobile along with the Flashcards at your own risk)!

Topics and code examples are lazy-loaded and may require two-clicks from the TOC to correctly calculate the updated x,y coordinates (after rendering). Thanks!

JavaScript: General Concepts

  1. JavaScript is an interpreted, often transpiled, Object Oriented programming language.
  2. Prototype-based Object Oriented Design with optional Class syntactic sugar.
  3. Dynamically Typed - JavaScript uses Type Coercion to Dynamically Type values and variables at Run Time.
  4. Technically, "JavaScript" is a loose name for a family of dialects that implement the ECMAScript standard: https://www.ecma-international.org/technical-committees/tc39/ - officially, there is no "JavaScript" but the name persists.
  5. JavaScript Primitive Data Types (Number, String, BigDecimal, Boolean, etc.) Pass by Value. Arrays and Objects Pass by Reference (one cause for the phenomenon of Shallow Copying).

Use Strict

Strict Mode enforces stricter/more secure execution of a Script at Run Time:

  1. Hoisting and assignment to variables that weren't declared result in syntax errors.
  2. this is Autoboxed more stringently.
  3. eval() is handled more securely.
  4. Silent errors that are typically ignored are thrown verbosely.
'use strict'

//...

Refer to: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode

JavaScript: Comparisons

Strict

Checks for exactly the same Value, Type, and Reference (Address in Memory):

a === b

Loose

Checks for the same Value after automatic conversion to a common Type (shared Type Coercion):

a == b

Object.is()

Special comparison for NaN and 0 along with some other important but "edgy" cases.

Refer to: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators

Type Checking

There are three primary ways to Type Check:

Primitive checking:

typeof 'Hello World' === 'string';

Check whether an Object is an instance of a Class:

var x = new Dog();
x instanceof Dog;

Via constructor:

var x = 'Hello World';
x.constructor === String;

JavaScript: This Keyword

The this keyword allows one to manipulate the current Closure/scope.

Arrow Functions

Note that JavaScript Arrow Functions don't bind this to their Closure/scope (this will refer to the next-nearest available surrounding scope).

const A = function() {

    this.example = "hello, world"

    const B = () => {
        // Refers to the scope of A
        console.log(this.example) 
    }

    B()
}() // "hello, world"

Scope Binding

Consider the following:

var meeple = "Meeple One"

var myObj = {
    meeple: "Meeple Two",
    prop: {
        getMeeple: function() {
            return this.meeple
        },
        meeple: "Meeple Three"
    }
}

console.log(myObj.prop.getMeeple())

var test = myObj.prop.getMeeple

console.log(test())

The output will be:

"Meeple Three"
"Meeple One"

Remember that this gets bound to the Scope of the function called - in the case of test() the Scope is the outer Closure since it's defined in the top-level Scope (of the script file itself).

Bind, Call, Apply

Since this is relative to the scope in which it's used, it's sometimes necessary to use a JavaScript Function with a precisely specified this value.

// Bind, React example
this.setSubmitState = this.setSubmitState.bind(this)

https://gitlab.com/Thoughtscript/card_lookup_helper/-/blob/main/client/reactAppSrc/Components/Stateful/Submit/index.jsx?ref_type=heads#L19

// Call, Prototype example
function Example(msg) {
  this.msg = msg
}

function SubExample(msg, subexamplefield) {
  Example.call(this, msg)
  this.subexamplefield = subexamplefield
}

console.log(new SubExample('my msg', 'my subexample field'))
// Apply, native Math function example w/ null this
const max = Math.max.apply(null, [5, 6, 2, 3, 7])
  1. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
  2. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply
  3. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call

Code samples:

  1. https://gitlab.com/Thoughtscript/card_lookup_helper/-/blob/main/client/reactAppSrc/Components/Stateful/Submit/index.jsx?ref_type=heads#L19

JavaScript: Falsy Values

Are interpreted as false:

    false  
    0 (zero)  
    "" (empty string)  
    null  
    undefined    
    NaN   

All values are truthy unless they are falsy.

JavaScript: Quirks

Some that are covered elsewhere:

  1. Shallow vs. Deep Copying
  2. Truthy and Falsy values (in general)
  3. Type Coercion (Auto-Boxing)

Empty Array Comparisons

Empty Array comparisons can result in some counter-intuitive consequences due to Type Coercion and Falsy Value resolution:

console.log([] === ![]) // false
console.log([] !== []) // true
console.log([] == ![]) // true

Default Numeric Sorting

One of JavaScript's oft-lamented quirks is that Number types are cast to Strings when sorted using default sorting:

const A = [1,2,3,4,5,6,7,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
A.sort()
console.log(A)
// [1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 2, 20, 3, 4, 5, 6, 7, 7, 8, 9]

However using a custom comparator that forces Numeric comparison:

//... 

A.sort((a,b) => {
    const A = parseInt(a), B = parseInt(b)
    if (A < B) return -1
    if (A > B) return 1
    return 0
})
//...

// [1, 2, 3, 4, 5, 6, 7, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]

Numeric Object Keys

Object key values don't have the above quirk. They are treated as Numbers:

const M = {}

M[1] = true
M[21] = true
M[2] = true
M[12] = true
M[3] = true
M[11] = true

const OK_M = Object.keys(M)

for (let i = 0; i < OK_M.length; i++) {
    console.log(`${OK_M[i]} ${M[OK_M[i]]}`)
}

/*
    "1 true"
    "2 true"
    "3 true"
    "11 true"
    "12 true"
    "21 true"
*/

JavaScript: Variables

  1. let - specifies a mutable variable declaration, scoped to the immediate local Closure scope.
  2. const - immutable once declared, specifies a Constant.
  3. var - original, general, not immutable, not scoped, Hoisted.

Advantages of Let vs. Var

Since let is scoped to a local context, doing the following:

for (let i = 0; i < els.length; i++) {
    els[i].addEventListener('click', function() {
        console.log(i)
    })
}

will correctly print the right number i. If var is used instead each button will only print els.length - 1.

JavaScript: Prototypes

JavaScript's Object Oriented

Examples

var Human = function(name) {
    this.name = name;
}

Human.prototype.getName = function() {
    return this.name;
}

Note that Prototype names must be written using Pascal Notation.

Object Oriented Design

Define a Prototype:

var Dog = function(bark) {
    this.bark = bark;
};

//Call constructor
var p = new Dog("woof");
// Add a method to Dog.prototype
Dog.prototype.tail = function(){
    console.log("wags");
};

Inheritance:

function Poodle(bark, name) {
    // Call the parent constructor
    Dog.call(this, bark);
    this.name = name;
};

Or explicitly set prototypical inheritance using Object.create():

//Explicitly set prototypical inheritance
//Allows overriding parent methods
Poodle.prototype = Object.create(Dog.prototype);

Test it out:

var d = new Poodle("bark bark", "dooder");
d.tail(); //"wags"

console.log(d instanceof Dog);  // true 
console.log(d instanceof Poodle); // true

JavaScript: Classes

class A {
    // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_classes#private_fields
    #privatefieldA = 2 // # affix indicates field
    #privatefieldB = -1 // # affix indicates field

    // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_classes#static_properties
    static staticfield = "I'm only accessible through Class A not an Instance of"

    // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_classes#accessor_fields
    get privatefieldA() {
        return this.#privatefieldA // Getter can expose privatefield like usual
    }
}

class B extends A {
    publicfieldB = "I'm publicfieldB"

    constructor(constructorfield = "I'm initialized!"){
        super() // Must be present before this
        this.publicfieldA = "I'm publicfieldA"
        this.constructorfield = constructorfield
    }
}

class C extends B {
    // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_classes#constructor
    constructor(){
        super("I'm initialized from the subclass!") // Must precede this 
    }

    static staticmethod() {
        return "I'm a static method!"
    }
}

const a = new A()
console.log(`Class A: ${A}`)
console.log(`Object a: ${JSON.stringify(a)}`)
console.log(A.staticfield)
//console.log(a.#privatefieldA) // error
console.log(a.privatefieldA) // is accessible through getter
//console.log(a.#privatefieldB) // error
console.log(a.privatefieldB === undefined) // no getter

const b = new B()
console.log(`Class B: ${B}`)
console.log(`Object b: ${JSON.stringify(b)}`)
console.log(b.publicfieldA)
console.log(b.publicfieldB)
console.log(b.constructorfield)
//console.log(b.#privatefieldA) // error
console.log(b.privatefieldB === undefined) // no getter

const c = new C()
console.log(`Class C: ${C}`)
console.log(`Object c: ${JSON.stringify(c)}`)
console.log(c.constructorfield)
console.log(C.staticmethod())
//console.log(c.#privatefieldA) // error
  1. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_classes

Code samples:

  1. https://github.com/Thoughtscript/languages_2024/blob/main/node/classes/ClassExample.js

JavaScript: Promises

  1. Promises - an Object for handling Asynchronous, non-blocking, operations.
  2. Async/Await - syntactic sugar for returning and handling a Promise

Examples

  1. Must wrap all await keywords within an async method.
const asyncFunc = async (x) => {
    return 2
}

const EX = async () => {
    const V = await asyncFunc(1)
    console.log(V) //2
}

EX()
const assignAsyncVal = () => new Promise((resolve, reject) => {
    asyncFunc(1).then(success => {
        return resolve(success)
    })
})

const V = assignAsyncVal().then(success => {
    console.log(success) //2
})

Using await with the Promise function assignAsyncVal() above:

const EX = async () => {
    const VV = await assignAsyncVal()
    console.log(VV) //2
}

EX()

Many Promises

To resolve multiple Promises, push them into an Array and use Promise.all(), Promise.allSettled(), etc.

const A = new Promise((resolve, reject) => {})
const B = new Promise((resolve, reject) => {})

const PROMISES = []
PROMISES.push(A)
PROMISES.push(B)

// Resolve all promises
Promise.all(PROMISES).then(success => {
    //...
})

Async Error Handling

Use .then() and .catch() for all Error and Rejection handling:

T()
    .then(success => { console.log(success) })
    .catch(ex => console.error(ex.message))

Note: don't pass , fail => { //... } in then() when using this syntax.

Use try-catch for blocking operations:

try {
    await T()
    //...

} catch (ex) {
    //...
}

Note: setTimeout() and other Asynchronous operations won't get handled by the catch block by itself.

Use process.on('uncaughtException', //...) to catch all (Synchronous or Asynchronous) unhandled Process-level Exceptions:

try {
    process.on('uncaughtException', exception => { console.error(exception}) })

} catch (ex) {
    //...
}

Consult: https://github.com/Thoughtscript/async_error_node for examples.

  1. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all

Code samples:

  1. https://github.com/Thoughtscript/async_error_node

JavaScript: Asynchronous Loops

Given the following helper method:

const print = str => {
  const el = document.getElementById("here");
  const txt = document.createTextNode(str);
  el.append(txt);
  const br = document.createElement("br");
  el.append(br);
  console.log(str);
};

Synchronous Loops

A few examples.

const firstExample = () => {
  let i;
  for (i = 0; i < 10; i++) {
    print("1st middle: " + i);
  }
  print("1st end: " + i);
};
firstExample();
const secondExample = () => {
  let i;
  for (i = 0; i < 10; i++) {
    print("2nd middle: " + i);
  }
  print("2nd end: " + i);
  return i;
};
let j = 0;
j = secondExample();
print("J 2nd: " + j);
const fourthExample = () => {
  let y = 0;
  for (let i = 0; i < 10; i++) {
    y++;
    print("4th middle: " + y);
  }
  print("4th end: " + y);
  return y;
};

let v = 0;
v = fourthExample();
print("V 4th: " + v);
1st middle: 0
1st middle: 1
1st middle: 2
1st middle: 3
1st middle: 4
1st middle: 5
1st middle: 6
1st middle: 7
1st middle: 8
1st middle: 9
1st end: 10

2nd middle: 0
2nd middle: 1
2nd middle: 2
2nd middle: 3
2nd middle: 4
2nd middle: 5
2nd middle: 6
2nd middle: 7
2nd middle: 8
2nd middle: 9
2nd end: 10
J 2nd: 10

4th middle: 1
4th middle: 2
4th middle: 3
4th middle: 4
4th middle: 5
4th middle: 6
4th middle: 7
4th middle: 8
4th middle: 9
4th middle: 10
4th end: 10
V 4th: 10

Asynchronous Loops

Given:

const thirdExample = () => {
  return setTimeout(() => {
    let y = 0;
    for (let i = 0; i < 10; i++) {
      y++;
    }
    print("3rd end: " + y);
    return y;
  }, 1000);
};
let z = 0;

z = thirdExample();
print("Z 3rd: " + z);

Intuitively, we think that the output should be something like:

// Incorrect assumption
3rd end: 10
Z 3rd: 10

Given:

const fifthExample = () => {
  let y = 0;
  setTimeout(() => {
    y = 10000;
  }, 4000);
  return y;
};

print("5th: " + fifthExample());

It's natural to think the output will be:

// Incorrect assumption
5th: 10000

The actual chronological output:

Z 3rd: 1

5th: 0

3rd end: 10

To remedy those counter-intuitive outputs wrap the methods with a Promise.

Event Stack

for (let i = 0; i < 3; i++) {
   console.log(1)
   setTimeout(() => console.log(i), 0)
   setTimeout(() => console.log(2), 0)
   setTimeout(() => console.log(3), 100)
   console.log(4)
}

In chronological order:

1
4
1
4
1
4

0
2
1
2
2
2

3
3
3

Perhaps counter-intuitively:

  1. Both console.log(1) and console.log(4) print before the rest (even the setTimeout() calls with 0 delay). This has to do with the way that JavaScript handles Events on the Stack. setTimeout() calls are lower priority Events and are executed last, even with a 0 delay.
  2. One might think that all console.log(3) calls would print prior to each console.log(4). They all occur after the fact. JavaScript essentially ignores any asynchronous blocking behavior within a loop.

Button Event Listeners

  1. Use let instead of var.
  2. Always lookup "fresh" info within the actual event callback rather than supplying some info at initialization since it will likely both be (1) out-of-date and (2) asynchronously invalid.

Refer to the article on JavaScript Variables.

JavaScript: Deep and Shallow Copies, Merging

Deep Copying creates a copy of an object that share no references in memory.

Shallow Copying creates a copy of an object that shares a reference in memory.

Deep Merging allows the deeply nested fields of two objects to be added to, combined, or merged with one of those objects.

From the Reference Documentation

“A deep copy of an object is a copy whose properties do not share the same references (point to the same underlying values) as those of the source object from which the copy was made. As a result, when you change either the source or the copy, you can be assured you're not causing the other object to change too; that is, you won't unintentionally be causing changes to the source or copy that you don't expect. That behavior contrasts with the behavior of a shallow copy, in which changes to either the source or the copy may also cause the other object to change too (because the two objects share the same references).”

Shallow Copying

Consider the following tensor of rank 1, a single dimension array:

const X = [1,2,3,4,5,6,7,8]
const Y = [...X]
X[0] = 1000
console.log(X) // [ 1000, 2, 3, 4, 5, 6, 7, 8 ]
console.log(Y) // [ 1, 2, 3, 4, 5, 6, 7, 8 ]

When the array being duplicated is of rank > 1:

const A = [[1,2,4,5,6,7,8],[1,2,4,5,6,7,8]]
const B = [...A]
A[0][0] = 1000
console.log(A) // [ [ 1000, 2, 4, 5, 6, 7, 8 ], [ 1, 2, 4, 5, 6, 7, 8 ] ]
console.log(B) // [ [ 1000, 2, 4, 5, 6, 7, 8 ], [ 1, 2, 4, 5, 6, 7, 8 ] ]

Deep Copying

The recommended way now is to use:

JSON.parse(JSON.stringify(ingredients_list)) // Object
const A = [[1,2,4,5,6,7,8],[1,2,4,5,6,7,8]] // Tensor of rank 2, matrix, m x n array where n > 1

// By element
const R = []
for (let r = 0; r < A.length; r++) {
    const row = []
    for (let c = 0; c < A[r].length; c++) {
            row.push(A[r][c])
    }
    R.push(row)
}

R[0][0] = 1000
console.log(R) // [ [ 1000, 2, 4, 5, 6, 7, 8 ], [ 1, 2, 4, 5, 6, 7, 8 ] ]
console.log(A) // [ [ 1, 2, 4, 5, 6, 7, 8 ], [ 1, 2, 4, 5, 6, 7, 8 ] ]
const A = [[1,2,4,5,6,7,8],[1,2,4,5,6,7,8]] // Tensor of rank 2, matrix, m x n array where n > 1

// By row spread operator
const R = []

for (let r = 0; r < A.length; r++) {
    // Single dimensioned arrays are deep copied by the spread operator.
    R.push([...A[r]])
}

R[0][0] = 1000
console.log(R) // [ [ 1000, 2, 4, 5, 6, 7, 8 ], [ 1, 2, 4, 5, 6, 7, 8 ] ]
console.log(A) // [ [ 1, 2, 4, 5, 6, 7, 8 ], [ 1, 2, 4, 5, 6, 7, 8 ] ]

Deep Merging

Object.assign({}, obj) 

JavaScript: Node

Example main method wrapped with try-catch clauses and Proccess exception handlers:

'use strict'

try {
  process.on('warning', warning => { console.error(`Warning encountered: ${warning}`) })
  process.on('unhandledRejection', rej => { console.error(`Unhandled Rejection override: ${rej}`) })
  process.on('uncaughtException', exception => { console.error(`Error encountered: ${exception}`) })
  process.on('exit', msg => { console.log(`Service shutting down: ${msg}`) })

//...

} catch (ex) {
  console.error(`Exception ${ex}!`)
  process.exit()
}

Workers

const {Worker } = require('worker_threads')

const createWorker = (filename, paramsToExecute) => new Promise((resolve, reject) => {

    //Constructor
    //Will pass paramsToExecute to the method executed in filename
    //Must have workerData as attribute
    const W = new Worker(filename, {workerData: paramsToExecute })

    //Listeners in parent thread
    W.on('message', message => {
        console.log(`Worker message received: ${message}!`)
        resolve(message)
    })

    W.on('error', error => {
        console.error(`Worker error encountered: ${error}!`)
        reject(error);
    })

    W.on('exit', exitCode => {
        if (exitCode !== 0) {
            console.error(`Worker stopped with exit code ${exitCode}`)
            reject(exitCode)
        } else {
            console.log(`Worker stopped with exit code ${exitCode}`)
            resolve(exitCode)
        }
    })

    //Send message to worker script
    W.postMessage('I am initialized...')

})

//Wrap this with a Thread Pool and/or Thread count to prevent excessive resourcing
const executeServiceUsingThread = (filename, paramsToExecute) => new Promise((resolve, reject) => {
    createWorker(filename, paramsToExecute).then(success => {
        console.log(`Service completed: ${success}!`)
    }, failure => {
        console.error(`Service completed: ${failure}!`)
    })
})

module.exports = {
    executeServiceUsingThread: executeServiceUsingThread
}
"use strict"

/**
 * Note: the WebWorker importScripts() cannot be used here.
 *
 * These are essentially NodeJS compliant scripts
 */

const { workerData, parentPort } = require('worker_threads')

//console.log test

console.log('Test console log inside Worker One')
console.log(`Getting Worker One workerData: ${workerData}`)

const conversationMappings = {
    "Hello!": "Goodbye!"
}

const exampleDependencyFunction = text => conversationMappings[text]

parentPort.postMessage(`Worker one: ${workerData} - message response: ${exampleDependencyFunction(workerData)}!`)
//...
WT.executeServiceUsingThread('./nodeThread/workerOne/worker_script_one.js', "Hello!")

HTTP

module.exports = {
  createHttpServer: () => {
    const S = require('http').createServer(require('./express').createServer())

    console.log('HTTP initialized!')
    console.log('REST API controller initialized!')

    S.on('clientError', (err, sck) => {
      const e = `HTTP/1.1 400 Bad Request! ${err}`
      console.error(e)
      sck.end(e)
    })

    S.listen(require('../config').SERVER.PORT, () => { console.log(`HTTP server started on port: ${S.address().port}`) })

    return S
  }
}

HTTPS

With Express:

const C = require('../config'), FS = require('node:fs')

module.exports = {
  createHttpsServer: () => {
    const OPTS = {
      key: FS.readFileSync(C.SERVER.SSL.KEY_PATH),
      cert: FS.readFileSync(C.SERVER.SSL.CERT_PATH)
    }
  
    const S = require('node:https').createServer(OPTS, require('./express').createServer())

    console.log('HTTPS initialized!')

    S.listen(C.SERVER.HTTPS_PORT, () => { console.log(`HTTPS server started on port: ${S.address().port}`) })

    return S
  }
}
const express = require('express'),
  C = require('../config')

module.exports = {
  createServer: () => {
    const app = express()

    app
      .use(require('morgan')('dev'))
      .use(express.json())
      .use(express.urlencoded({ extended: true }))

      .use(require('cookie-parser')())

      .use(require('cors')({
        origin: C.SERVER.CORS,
        optionsSuccessStatus: 200
      }))
      
      .use('/api', require('./api'))

    return app
  }
}
const express = require('express'),
  privateApi = express.Router(),
  C = require('../config')

privateApi
  .all('*', async (req, res, next) => await require('./auth').LOG_IP_FILTER(req, res, next))

  .all('*', async (req, res, next) => await require('./auth').DECRYPT(req, res, next))

  .all('*', async (req, res, next) => await require('./auth').AUTH_CHECK(req, res, next))

  // https://localhost:8888/api/projections?auth=...
  .get("/projections", async (req, res) => {
    let responseData = await require('./domain/budgetprojections').BUDGET_PROJECTIONS({})
    let stringData = require('./auth').SCRAMBLE(C.AUTH.V(), C.AUTH.BP(), JSON.stringify(responseData))
    return res.send({ status: 200, data: stringData })
  })

  //...

Exports and Imports

module.exports = {
    myExportedFunction: () => {
      //...
    }
}
const R = require('../path/to/my/file.js')

R.myExportedFunction()

Code samples:

  1. https://github.com/Thoughtscript/mean_2023/tree/main/node
  2. https://github.com/Thoughtscript/kin_insurance_js
  3. https://github.com/Thoughtscript/node-2021
  4. https://github.com/Thoughtscript/node_apis_notes

JavaScript: Imports

Named Imports

export const

export const
    BASE_PATH = '/',
    HOME_PATH = '/home',
    API_PATH = '/api',
    RAILS_API_URL = 'http://localhost:3000/examples/all'
import { BASE_PATH, HOME_PATH, API_PATH } from '../../../Constants'

Default Imports

export default

//...

export default () =>
    <header>
        <h2>React 2024</h2>
        <Menu/>
    </header>
import CustomHeader from '../../Presentation/CustomHeader'

Namespace Imports

Resolve namespace conflicts, assign a custom monniker, or get all exports from a namespace, etc.

//...
import * as my_alias from 'my_library'

Side Effects and Files

Import to load, execute, or use a script, file, or asset.

import 'my_script'

import './MyCoin.css'

CommonJS and ES Modules

Node

module.exports = {
    MY_EXPORT: //..
}
const A = require('my_module').MY_EXPORT

Consult:

  1. https://nodejs.org/api/esm.html#require
  2. https://nodejs.org/api/modules.html
  1. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import
  2. https://nodejs.org/api/esm.html#require
  3. https://nodejs.org/api/modules.html

Code samples:

  1. https://github.com/Thoughtscript/rrr_2024/tree/main/react/reactAppSrc
  2. https://github.com/Thoughtscript/erc20_2024/blob/main/react/reactAppSrc/Components/Stateful/MyCoinPage/index.jsx

JavaScript: Typescript

Interfaces

// Interfaces are struct-like with typed constraints
export interface Example {
    fieldA: string
    fieldB: number
    fieldC: boolean
}

// variable types are specified via:
const E: Example = {
    fieldA: "example",
    fieldB: 1,
    fieldC: true
}

// interfaces can be combined with class and constructor syntax
export class Example implements Example {
    fieldA = ""
    fieldB = 0
    fieldC = false

    constructor (fieldA: string, fieldB: number, fieldC: boolean) {
      this.fieldA = fieldA
      this.fieldB = fieldB
      this.fieldC = fieldC
    }
}

const EE = new Example('a', 3, true)

https://github.com/Thoughtscript/languages_2024/tree/main/typescript/src/interfaces

Types

type A = {
    id: number
}

type B = {
    name: string
}

// Types can be combined (intersect, union, simple polymorphism, etc.)
type C = A & B

interface D {
    fieldA: string,
    fieldB: string,
    fieldC: number,
}

// Can pick specific fields (think subtype)
type E = Pick<D, "fieldA">;

const A: A = {
    id: 1
}

const C: C = {
    id: 1,
    name: "pronomen"
}

const E: E = {
    fieldA: 'message'
}

https://github.com/Thoughtscript/languages_2024/tree/main/typescript/src/types

Error Suppression and Inline Strictness

To be used sparingly (most Production codebases I've seen have this somewher - for example here):

// @ts-ignore

The above will suppress the specified TypeScript Error originating from the following line.

Inline Strictness and Error Suppression

  1. https://www.typescriptlang.org/docs/
  2. https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-6.html#suppress-errors-in-ts-files-using--ts-ignore-comments
  3. https://www.typescriptlang.org/docs/handbook/intro-to-js-ts.html#ts-check

Code samples:

  1. https://github.com/Thoughtscript/ts_serverless_exp/tree/master
  2. https://github.com/Thoughtscript/languages_2024/tree/main/typescript
  3. https://github.com/Thoughtscript/typescript_classes
  4. https://github.com/Thoughtscript/pyng_2025/tree/main/ng

JavaScript: Web Workers

WorkerManager

<html>
<head>
    <meta charset='UTF-8'>
    <title>WebWorker Example</title>
</head>
    <body>
        <h3>WebWorker Request:</h3>
        <h id="request">Hello!</h>
        <h3>WebWorker Responses:</h3>
        <p id="response"></p>
    </body>
    <script type="text/javascript" src="workerManager.js"></script>
    <script>
        var wm = new WorkerManager();
        wm.startWrapperExample(document.getElementById("request").textContent);
    </script>
</html>

Using a WorkerManager wrapper with two example implementations: BlobWorker and ScriptWorker.

//workerManager.js

"use strict";

/** BEGIN WorkerManager Prototype */

var WorkerManager = function () {
};

WorkerManager.prototype.Worker = null;

WorkerManager.prototype.opts = {
    inline: false,
    scriptWorker: {
        script: "scriptWorker.js"
    },
    blobWorker: "self.addEventListener('message', function(e) {" +
    "self.postMessage('Goodbye!');" +
    "}, false);" 
};

WorkerManager.prototype.createBlobWorker = function () {
    var blob = window.URL.createObjectURL(new Blob([WorkerManager.prototype.opts.blobWorker]));
    this.Worker = new Worker(blob);
};

WorkerManager.prototype.createScriptWorker = function () {
    this.Worker = new Worker(this.opts.scriptWorker.script);
};

WorkerManager.prototype.isSupported = function () {
    return window.Worker;
};

WorkerManager.prototype.startWrapperExample = function (t) {
    if (this.isSupported()) {
        if (this.opts.inline) this.createBlobWorker();
        else this.createScriptWorker();
        if (this.Worker != null) {
            this.Worker.postMessage(t);
            this.Worker.addEventListener('message', function (e) {
                document.getElementById('response').textContent = e.data;
            }, false);
        }
    }
};

/** END WorkerManager Prototype */

ScriptWorker

//scriptWorker.js

"use strict";

importScripts('./scriptWorkerDependency.js');

self.addEventListener('message', function (e) {
    self.postMessage(exampleDependencyFunction(e.data));
}, false);

ScriptWorker Dependency

// scriptWorkerDependency.js

"use strict";

var conversationMappings = {
    "Hello!": "Goodbye!"
};

var exampleDependencyFunction = function(text) {
    return conversationMappings[text];
};

JavaScript: Techniques

Initialize Many EventListeners

To initialize an EventListener on a DOM Element:

const el = document.getElementById('todos')

for (let i = 0; i < el.children.length; i++) {
    const c = el.children[i]

    c.addEventListener('click', (e) => {
        console.log(e.target.id)
        e.target.style.display = 'none'
        e.preventDefault
    })
}

Remember that by the time the Element is clicked:

  1. c will lose its value - it's disassociated with the Element by that point.
  2. e.target is still the preferred, official, way to access the Attributes on the Element, the Element itself, etc.
  3. this will refer to the top-level scope and shouldn't be used.

In short, use e and e.target to access the propagated DOM Event and current Element, respectively. (Similar to React's ref.current.)

Fastest Way To Remove An Index

Supposedly the fastest way to remove an index in an array is as follows:

const todos = []

for (let i = 0; i < todos.length; i++) {
    if (todos[i].uuid === uuid) {
        // Remove by index
        todos.splice(i, 1)
        break
    }
}

Vs. deep removal:

const test = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, "a", 'b', 'c', 'd', 'e', "f", 'g']

const deepRmv = key => {
  const temp = [...test]
  let result = []

  const BEGIN = new Date()
  for (let i = 0; i < temp.length; i++) {
    if (temp[i] === key) continue
    result.push(temp[i])
  }
  const END = new Date()

  console.log(`deepRmv time: ${END-BEGIN} result: ${result}`)

  return result
}

const rmvBySlice = key => {
  const temp = [...test]

  const BEGIN = new Date()
  for (let i = 0; i < temp.length; i++) {
    if (temp[i] === key) {
      temp.splice(i, 1)
      break
    }
  }
  const END = new Date()

  console.log(`rmvBySlice time: ${END-BEGIN} result: ${temp}`)

  return temp
}

deepRmv("d")
rmvBySlice("d")
"deepRmv time: 0 result: 1,2,3,4,5,6,7,8,9,0,a,b,c,e,f,g"
"rmvBySlice time: 0 result: 1,2,3,4,5,6,7,8,9,0,a,b,c,e,f,g"

Probably best to use for loop with splice since a tad more succinct writing-wise.

https://javascript.plainenglish.io/how-to-remove-a-specific-item-from-an-array-in-javascript-a49b108404c shows that for loop with splice is 5x faster than indexOf with splice.

Common Operations

const ARR = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

const first = ARR.shift() // shift - remove and return element from index 0
console.log(first) // 1
console.log(ARR) // [2, 3, 4, 5, 6, 7, 8, 9, 10]

ARR.unshift(first) // unshift - add element to index 0
console.log(ARR) // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

ARR.splice(4, 5) // splice(i, j) - remove j element(s) from and including i
console.log(ARR) // [1, 2, 3, 4, 10]

ARR.splice(3, 0, 11) // splice(i, j) - remove j element(s) from and including i
console.log(ARR) // [1, 2, 3, 11, 4, 10]

const sliced = ARR.slice(0, 3) // slice(i, j) - return elements between i and j (inclusive-exclusive) without altering ARR
console.log(sliced) // [1, 2, 3]
console.log(ARR) // [1, 2, 3, 11, 4, 10]
  1. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift
  2. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/unshift
  3. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice
  4. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice

Type and Instance Checking

const A = 999, B = "I'm a String.", C = [1, 2, 3]

console.log(Number.isNaN(A)) // false
console.log(Number.isNaN(B)) // false
console.log(Number.isNaN(C)) // false

console.log(typeof A) // "number"
console.log(typeof B) // "string"
console.log(typeof C) // "object"

console.log(A instanceof Number) // false
console.log(B instanceof Object) // false
console.log(B instanceof String) // false
console.log(C instanceof Array) // true
console.log(C instanceof Object) // true

Benchmarking

A cool benchmarking site: https://jsbench.me/nyla6xchf4/1

BigInt

Number to BigInt conversion:

const A = 9007199254740991n
const B = BigInt(9007199254740991)
const C = 1n
const D = C / B
  1. https://javascript.plainenglish.io/how-to-remove-a-specific-item-from-an-array-in-javascript-a49b108404c
  2. https://jsbench.me/
  3. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift
  4. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/unshift
  5. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice
  6. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice