JavaScript Idioms - sgml/signature GitHub Wiki

Low-Level

ECMAScript 2021 Keywords and Alternative Implementations

Keyword Alternative Implementation
catch Promise.catch()
class Constructor functions + Object.create()
const Object.defineProperty(obj, 'x', { value: ..., writable: false })
delete Reflect.deleteProperty(obj, key)
export module.exports = { ... } (Node.js)
extends Object.setPrototypeOf(Sub.prototype, Super.prototype)
finally Promise.finally()
for Array.prototype.forEach()
function const f = function(...) { ... }
if cond && fn()
import require('module') (Node.js)
in Reflect.has(obj, key)
instanceof obj.constructor === Type
let var with block scoping simulated via closures
new Object.create(Constructor.prototype) + Constructor.call(obj)
super Parent.prototype.method.call(this, ...)
this Explicit parameter self passed into functions
throw Promise.reject(error)
try Promise.resolve().catch(...)
typeof Object.prototype.toString.call(obj)
with Object.assign(context, {...})
yield Iterator objects
async Promise chains
await Promise.then()

Assumptions

Assumptions about logic should be expressed in comments

Assumptions about template output should be expressed in CSS dimensions

Assumptions about data binding should be expressed in functions

Add comments outlining expected DOM behavior based on state

Add specific DOM IDs to track specific state changes

Add two booleans to make sure conditional logic always tests for something true, rather than coupling a falsy state with fallback behavior

Use read-only attributes to cover paradoxical or ambiguous state combinations, rather than leaving editable fields in states where they shouldn't be editable

Any config file that accepts arrays, also accepts a function that returns an array. Use that instead; be explicit

Overuse

Use Backbone as a model for a generic caching layer instead of library specific persistence methods (component props, slots, store APIs, etc.)

Use key/value pairs, not arrays with a fixed number of boolean (two) or ternary(three) indices

Use CSS for creating UI; do not depend on it (zindex esp)

Use HTTP statuses instead of traversing response XML/JSON

Use URLs for tracking history

Use HTML for storing metadata

Use constants instead of calling endpoints for database values that do not change, such as statuses

Use callbacks <input onclick=foo()> even if templates can include inline logic <input :click="a + b > 0 ? go() : stay()">

Underuse

Use named functions

Use [].length = 2 to truncate an array

Use refactoring tools such as the JS Refactoring Assistant to move anonymous functions, inline functions, and ternary logic out of templates BEFORE doing any refactoring or wholesale framework migration

Use anything but fat arrow functions(farts)

Static File Generation

Use pnpm generate to test whether or not there are syntax errors during static file generation

Type Checking

Use multimethods or multiple-dispatch to encapsulate type checking of API request/response payloads

Reflection

Use reflection accept any data type as an arg, then to do different things based on the type, which is Postel's law:

/* Define mapping */
var app = {"emit": emit};

/* Define interface */
function emit(){}

function \u1000missing(object, method, fallback)
  {
  /* Existence check */
  if (/function/.test(object[method]) ) 
    {
    object[method]();
    }
  /* reify */
  else
    {
    object[fallback](method)
    }    
  }

\u1000missing(app,"isReady","emit")

Decoupling

Split out the following JavaScript constructs into files:

  1. Constants
  2. API Endpoints/GraphQL Queries
  3. Templating Logic
  4. JSON Schema Definition
  5. DOM Event Callback Functions (Load/Unload)
  6. AJAX Event Callback Functions (Request/Response)
  7. Persistence (LocalStorage/Caching/Store Libraries)

Patterns

try { set loading boolean to true, fetch data, set state } catch { throw errors } finally { reset loading boolean to false }

function foo(a,b,c) { return a + b + c > 3 } // instead of a && b && c

Names

Use generic schema.org names for JSON keys (Context, ID, Type):

{
"@context": {
        "name": "http://xmlns.com/foaf/0.1/name",
        "homepage": {
            "@id": "http://xmlns.com/foaf/0.1/workplaceHomepage",
            "@type": "@id"
        },
        "Person": "http://xmlns.com/foaf/0.1/Person"
    },
    "@id": "https://www.linkeddatatools.com/johndoe",
    "@type": "Person",
    "name": "John Doe",
    "homepage": "https://www.linkeddatatools.com/"
}

Use a custom JSON parser

function customReviver(key, value) {
    if (key === 'email' || key === 'phone') {
        // Modify the value as needed
        // For example, return a boolean value
        return value === 'true';
    }
    return value; // Pass through unmodified values
}

const jsonString = '"{\\"email\\": true, \\"phone\\": true}}"';
const parsedObject = JSON.parse(jsonString, customReviver);

console.log(parsedObject); // Outputs: { email: true, phone: true }

Use VBScript Naming Conventions:

Begin each separate word in a name with a capital letter, as in FindLastRecord and RedrawMyForm.

    Begin function and method names with a verb, as in InitNameArray or CloseDialog.

    Begin class, structure, module, and property names with a noun, as in EmployeeName or CarAccessory.

    Begin interface names with the prefix "I", followed by a noun or a noun phrase, like IComponent, or with an adjective describing the interface's behavior, like IPersistable. Do not use the underscore, and use abbreviations sparingly, because abbreviations can cause confusion.

    Begin event handler names with a noun describing the type of event followed by the "EventHandler" suffix, as in "MouseEventHandler".

    In names of event argument classes, include the "EventArgs" suffix.

    If an event has a concept of "before" or "after," use a suffix in present or past tense, as in "ControlAdd" or "ControlAdded".

    For long or frequently used terms, use abbreviations to keep name lengths reasonable, for example, "HTML", instead of "Hypertext Markup Language". In general, variable names greater than 32 characters are difficult to read on a monitor set to a low resolution. Also, make sure your abbreviations are consistent throughout the entire application. Randomly switching in a project between "HTML" and "Hypertext Markup Language" can lead to confusion.

    Avoid using names in an inner scope that are the same as names in an outer scope. Errors can result if the wrong variable is accessed. If a conflict occurs between a variable and the keyword of the same name, you must identify the keyword by preceding it with the appropriate type library. For example, if you have a variable called Date, you can use the intrinsic Date function only by calling [DateTime.Date](https://learn.microsoft.com/en-us/dotnet/api/system.datetime.date).

Async

The Uncaught (in promise) error can be avoided by having try/catch/finally blocks within each promise and returning the payload in each finally block to the endpoint that failed

Use the async await pattern if calls are non-blocking; make sure you do not await a non-async function, it will fail silently

Use a global loading state array rather than individual component based state tracking

Use promise error handling to setup and track retries

Linting

Run linting on each file before you commit it

Defensive Programming

  • Use a constants file instead of string sprawl
  • Use a custom Error object to handle map/filter/reduce errors

Debugging

Use a search to find missing key/value pairs as follows:

  • Lookup a string value
  • Copy the key name of the string value
  • Lookup the key name
  • Copy the method(s) that use the key name
  • Add if/else logic to fix the bug in question
  • Add/Update tests to verify the new logic
  • Verify tests in the CI/CD tool

Use test specs to debug your use of reusable components to make sure your assumptions are correct

Exceptions

Use the pause on caught exceptions feature in the developer tools to catch type mismatches

Comments

Add logging to every local variable and argument

Add logging to every function and loop

Point out code which has complex conditional logic or regex patterns for in-depth commenting

JSDoc

Use JSDoc annotations to do regular expression checking:

/**
 * @param {RegExp} regex - Regular expression to test
 * @param {string} input - Input string to match against
 * @returns {boolean} - Whether the input matches the regex
 */
function testRegex(regex, input) {
    return regex.test(input);
}

Security by Obscurity

  • Using modals or CSS transforms as a security mechanism is childsplay to reverse engineering. Easy to build, easy to break
  • Using cookies for security is an anti-pattern.
  • Fix security issues by fixing the HTML generation, so there is nothing to obscure.

This Instance Context

Use this to store references to any framework specific instances such as modals or window specific instances such as setTimeout or setInterval to clear them independent of any exceptions in a finally block

Overriding DOM API Defaults

  • Define why the API methods that need to be overriden
  • If it is a framework specific limitation, change the framework
  • If not, document the override in the code
  • Implement the override using standard getter/setter logic which implements the interceptor pattern

Data Binding

Learn the underlying mechanism used for databinding in order to intercept and customize it. For example:

summary:
  - version: Vue 2
    mechanism: Object.defineProperty
    api_methods:
      - get
      - set
    watcher_effect_details: Watcher: Observes data changes and queues re-renders
    next_tick_details: Uses `nextTick` to batch and schedule DOM updates in the next event loop tick

  - version: Vue 3
    mechanism: Proxies
    api_methods:
      - Proxy
      - Reflect.get
      - Reflect.set
    watcher_effect_details: Effect: Tracks dependencies and schedules re-renders efficiently
    next_tick_details: Uses `nextTick` to batch and schedule DOM updates in the next event loop tick

Use computed properties instead of watchers:

    currentValue: {
      get() {
        return this.value
      },
      set(value) {
        this.$emit('input', value)
      },

Defensive Coding

Use a dictionary to store a primary and secondary value for every constant since the value itself cannot change, but its children can:

  function bindingData() {
        const data = {}
        if (this.foo?.data?.length) {
            data.primary = this.foo.data
            } else {
               data.secondary = []
            }
 
            return data?.primary || data?.secondary
        }

Use optional chaining to do existence checking when referencing nested data

        if (variables?.txa?.searchTerm) {

            const response = await this.$axios.$post('/GRAPHQL_URL/', {

            query,

            variables,

            })

Use recursion to loop through objects instead of dot notation and nested if/else, optional chaining foo?.bar, or guard operator foo && foo.bar logic:

Object Literals

  • Use object literals to create hashed array trees

  • Use object literals to create dictionaries to avoid spelling errors

Array Generics

Use a generic method to loop through arrays of objects:

function getArrayOfObjects(obj, objName) {
  let current

  for (current in obj) {
    if (getArrayOfObjects.id === undefined) {
      getArrayOfObjects.id = ""
    }
    if (!!obj[current] || obj[current] === "") {
      if (
        /Function|String|Object|Array/.test(String(obj[current].constructor))
      ) {
        if (getArrayOfObjects?.data) {
          getArrayOfObjects.data.push(objName + " => " + current)
        } else {
          getArrayOfObjects.data = []
        }
      }

      if (/String|Object|Function/.test(String(obj[current].constructor))) {
        getArrayOfObjects(obj[current], getArrayOfObjects.id + current)
      } else {
        console.log(obj[current]?.length)
        if (obj[current]?.length) {
          if (obj[current].length > 0) {
            getArrayOfObjects.id = obj[current]
          }
        }
      }
    }
  }
}

Use a generic function for parsing arrays instead of array specific methods:

function arrayChecker(arr) {
    console.assert([].every(function(){}))
}

Single Responsibility

Use dedicated functions instead of mixing templates with logic. Instead of this:

<div>
    <Foo />
<div :else>
    <Bar />
</div>

Do this:

<component :is="dyn">

<script>
import Foo
import Bar

function dyn(){
    if(a) {
       return 'foo'
    } else {
       return 'bar'
    }
}

Use let and simple if/else logic instead of const and inline ternary statements

  const locale = 'en'
  let foo = null
  let bar = null
  let baz = null

  try {
      if(locale === 'en'){
          foo = 'Arabic'
          bar = 'Phonetic'
          baz = 'Egyptian'
      } else {
          foo = 'Latin'
          bar = 'Etruscan'
          baz = 'Raetic'
      }
  } catch(e) {
      console.log('Error assigning value: ', e)
  } finally {
      console.log('foo: ', foo)
      console.log('bar: ', bar)
      console.log('baz: ', baz)
  }
      
  const summary = [
    {
      key: 'book',
      value: foo
    },
    {
      key: 'scroll',
      value: bar
    },
    {
      key: 'stone tablet',
      value: baz
    }
  ]

Simple Logic

Avoid ternary statements ?: as well as inline conditional expressions using && and ||.

Refactor the chain of boolean checks where there are more than two in the same statement, and update the API to return an aggregated response (sum, diff) instead of multiple values

Use try/catch/finally logic for code consistency, clarity and simplicity.

Use screenreader only classes to hide unconditional HTML for debugging purposes

Simple Forms

Use a subset of form controls (select, radio, checkbox), no default values, and no hardcoded options

Use an accordion to hide search boxes and other input fields by default to allow the JavaScript code to load before interaction is allowed

Simple Traceability

Use DOM event handlers instead of components for form controls (no button components please)

<div id="previous-page" @click="changePage">

changePage(event) {
  try {
      console.log('event: ', event)
      const bar = foo + (event.target.id == 'baz') ? 1 : -1
  } catch (e) {
      console.log(e)
  } finally {
      console.log(event)
  }

Modernization

const { parse, replace, generate } = require('abstract-syntax-tree');

// Read the old source code
const oldSourceCode = fs.readFileSync('old.js', 'utf8');

// Parse the source code into an AST
const ast = parse(oldSourceCode);

// Replace jQuery window statements with native JavaScript window statements
replace(ast, {
    enter(node) {
        if (node.type === 'MemberExpression' && node.object.name === '$' && node.property.name === 'window') {
            node.object.name = 'window';
        }
    }
});

// Generate the new source code from the AST
const newSourceCode = generate(ast);

// Write the new source code
fs.writeFileSync('new.js', newSourceCode);

Tracing

function traceMethodCalls(obj) {
    const handler = {
        get(target, propKey, receiver) {
            const targetValue = Reflect.get(target, propKey, receiver);
            if (typeof targetValue === 'function') {
                return function (...args) {
                    console.log('CALL', propKey, args);
                    return targetValue.apply(this, args); // (A)
                }
            } else {
                return targetValue;
            }
        }
    };
    return new Proxy(obj, handler);    
}

Testability

Add IDs and template refs for all buttons that handle events for testability and use the name of the method which handles the event as the value of the ID for ease of use

Use assert as a test runner:

// main.js
const obj = {};
obj.sum = (a, b) => {
    return a + b;
};
module.exports = obj;

// test.js
const main = require('./main.js');
const assert = require('assert');

const it = (desc, fn) => {
    try {
        fn();
        console.log('\x1b[32m%s\x1b[0m', `\u2714 ${desc}`);
    } catch (error) {
        console.log('\n');
        console.log('\x1b[31m%s\x1b[0m', `\u2718 ${desc}`);
        console.error(error);
    }
};

it('should return the sum of two numbers', () => {
    assert.strictEqual(main.sum(5, 10), 15);
});

And call it using a script tag:

// app.js
self.myapp = myapp; // All the methods in myapp will be exposed globally
myapp.sum = function(a, b) {
    return a + b;
};
 // test.html (your test runner for the front end)
<html>
    <body>
        <script src="app.js"></script>
        <script src="test.js"></script>
    </body>
</html>

Structured Clone Algorithm

START
  |
  v
CHECK: Is input value primitive?
  |
  +--> [YES] RETURN value directly (primitive types are immutable)
  |
  +--> [NO]
          |
          v
CHECK: Is the value a supported object type? 
  (e.g., Array, Object, Map, Set, Date, etc.)
          |
          +--> [NO] THROW error (Unsupported type: e.g., Function, Symbol, DOM node)
          |
          +--> [YES]
                  |
                  v
CREATE: Initialize a clone object/container of the same type
                  |
                  v
CHECK: Is the value already being cloned? (circular reference)
  |
  +--> [YES] RETURN existing cloned reference
  |
  +--> [NO]
          |
          v
MARK: Add the object to a "cloning map" to track circular references
          |
          v
FOR EACH key-value pair or element in the object:
  |
  +--> RECURSIVELY APPLY structured clone to each value
  |       |
  |       v
  |   STORE result in the cloned object
          |
          v
FINISH: Return the fully cloned object or data structure

Debugging

Use dev-mode to render reactive data in the UI for debugging purposes: https://github.com/fii-org/shared.web.vue.components/commit/6e0e5a51ca72235b81146ce4c86067e53a054cc9

Use the template output {{ }} to print values that need to be debugged. Wrap them in a div with an ID of hidden to make them easy to find via XPath in the developer tools

Mockups

Use web based editors to mockup things quickly:

Vue: https://www.tutorialspoint.com/online_vuejs_editor.php

Ember: https://ember-twiddle.com/

Buefy: https://codepen.io/tag/buefy

Bulma: https://codepen.io/tag/bulma

Tailwind: https://codepen.io/topic/tailwind/picks

CLI

Use CLI commands to decouple HTML, JS, CSS, transpilers, and preprocessors

Tailwind CLI

npx tailwindcss -i ./styles.css -o ./output.css --minify

References

Vue Template Refs

Vue Dynamic Async Components

How to Use Typescript to Point to a Function

Is there a way to apply CSS style on a datalist

Interceptors

JavaScript Interceptor Mechanisms

Intercepting and modifying built-in JavaScript behaviors using Reflect API and Proxy API.

Available Mechanisms

Mechanism Description Example
Monkey Patching Modifies built-in functions at runtime to override behavior. console.log = function(...args) { alert('Intercepted: ' + args); }
Proxy API Creates a wrapper that intercepts object property access and function calls. const proxy = new Proxy(target, handler);
Reflect API Provides fine-grained control over object interactions. Reflect.get(target, key) can be intercepted via Proxy.
Mutation Observers Watches for DOM structure changes and modifies elements dynamically. new MutationObserver(callback).observe(targetNode, { childList: true, subtree: true });
Event Delegation Captures events at a higher level to modify behavior before reaching the target. document.addEventListener('click', (event) => { event.stopPropagation(); console.log('Event intercepted'); });
Object.defineProperty Overrides properties or methods to control access dynamically. Object.defineProperty(window, 'localStorage', { get: () => null });
CSS Injectors Modifies styles dynamically to override default rendering. document.styleSheets[0].insertRule('.hidden { display: none; }', 0);
Shadow DOM Manipulation Isolates element styling and behavior while enabling controlled overrides. const shadowRoot = element.attachShadow({ mode: 'open' });

Proxy+Reflection

const targetObject = {
    name: "Intercepted Object",
    value: 42,
    getData() {
        return `Data: ${this.value}`;
    }
};

// Create a Proxy handler using Reflect
const handler = {
    get(target, prop) {
        console.log(`Intercepting GET: ${prop}`);
        return Reflect.get(target, prop);
    },
    set(target, prop, value) {
        console.log(`Intercepting SET: ${prop} = ${value}`);
        return Reflect.set(target, prop, value);
    },
    apply(target, thisArg, args) {
        console.log(`Intercepting CALL: ${target.name} with args ${args}`);
        return Reflect.apply(target, thisArg, args);
    }
};

// Apply the Proxy to intercept the object
const proxyObject = new Proxy(targetObject, handler);

// Example Usage:
console.log(proxyObject.name); // Intercepting GET: name
proxyObject.value = 100;       // Intercepting SET: value = 100
console.log(proxyObject.getData()); // Intercepting CALL: getData()

Monadic JS

Passing arguments to a given callback function within a particular scope can be generalized using a monad:

/* Unit function */
function Monad(value)
  {
  // Construct monad and set value to given argument or undefined
  this.value = value || undefined;
  }

/* Constructor function */
Monad.prototype.pass = function(value, cb, scope)
  {
  // return constructor result if no default value is passed
  if (/undefined/.test(this.value) )
    {
    return new this.constructor();
    }
  // return callback result for given value in given context if scope is passed
  if(scope)
    {
    /* Bind function */
    return cb.call(scope, value);
    }
  // return callback result for given value otherwise
  return cb(value);
  }

 /* Separate arguments from function, and function from global scope */
 var foo = new Monad(RegExp);
 var bar = foo.pass(2, Function("count","return count++")

Use dataURIs for everything it can be used for

Use hooks in realtime user monitoring to do global event capture:

import { datadogRum } from '@datadog/browser-rum';

datadogRum.init({
  applicationId: 'YOUR_APP_ID',
  clientToken: 'YOUR_CLIENT_TOKEN',
  site: 'datadoghq.com',
  service: 'your-service-name',
  env: 'production',
  version: '1.0.0',
  sampleRate: 100,
  trackInteractions: true,
  trackResources: true,
  trackLongTasks: true,
  beforeSend: (event) => {
    if (event.type === 'resource' && event.resource.type === 'xhr') {
      // Axios uses XMLHttpRequest under the hood
      console.log('Captured Axios request:', event);
      // You can also extract trace_id if available
      if (event._dd?.trace_id) {
        console.log('Trace ID:', event._dd.trace_id);
      }
    }
  }
});

Simple Assertions

/*const assert = require('assert');*/

(function hi(){
  hi.foo = Function;
  /*
  try
    {
    assert.ok(typeof hi.foo === 'function', 'Not a function');
    assert.ok(typeof hi.foo === 'string', 'Not a string');
    }
  catch(e)
    {
    console.log(e.message);
    }
  */
  }
)();

Syntactic Sugars

GitHub Issue (linkified) Alternative using Function constructor (JSLint‑style) ECMAScript‑4 equivalent ES4 spec section ES5 spec section
Not Awesome: ES6 Classes — curated list arguing class syntax was a mistake var User = new Function("name", "return { name: name, greet: function () { return 'Hi ' + name; } };"); class User { var name:String; function User(name:String) { this.name = name; } function greet():String { return "Hi " + this.name; } } ES4 Draft §4.2 Classes and Inheritance ES5 §15.3 Function Objects, §8.10 Property Attributes
Confusing wording on ES5 vs ES6 constructors — highlights confusion between function constructors and class syntax var Bird = new Function("", "return { fly: function () { return 'Flying!'; } };"); class Bird { function fly():String { return "Flying!"; } } ES4 Draft §4.2.2 Method Definitions ES5 §15.3.5.3 Call internal method
We are better off avoiding ES6 classes — argues plain objects/factories are clearer var Counter = new Function("", "var n = 0; return { inc: function () { n += 1; return n; }, val: function () { return n; } };"); class Counter { var n:int = 0; function inc():int { return ++this.n; } function val():int { return this.n; } } ES4 Draft §4.2.3 Fields and Properties ES5 §15.3.5.4 Construct internal method
Backbone and ES6 Classes — describes incompatibility of ES6 classes with Backbone’s patterns var Dog = new Function("name", "return { name: name, bark: function () { return 'Woof'; } };"); class Dog { var name:String; function Dog(name:String) { this.name = name; } function bark():String { return "Woof"; } } ES4 Draft §4.2.1 Class Declarations ES5 §15.3.4.5 Function.prototype.bind

Minimalist

  • Semantic HTML only — no

    ,

    , or
  • Every DOM element has a unique id
  • Microformats for contact, events, and reviews
  • Polyglot HTML (valid as both HTML and XHTML)
  • No modals, popups, AJAX, or CORS
  • JavaScript limited to bookmarklets only
  • CSS styling restricted to ,
      /
        , and
      1. Accessibility limited to and elements
      2. Use semantic containers like , , , , ,
      3. Use
        and and a la Stackoverflow
      4. Quote all attributes
      5. Include XML declaration if needed
      6. Use only
    for layout and alignment
  • Use
      /
        for lists and navigation
  • Use
    for emphasis and framing
  • Avoid class-based styling; use element selectors and id-based overrides
  • No flexbox, grid, or media queries
  • JavaScript must be executable via javascript: URLs
  • Bookmarklets may manipulate DOM by id, toggle visibility, or scroll to anchors
  • External script injection via bookmarklet is allowed
  • Bookmarklets must not store data or alter session state
  • No event listeners or mutation observers
  • No global variable pollution or navigation alteration
  • Bookmarklets must be non-disruptive and idempotent
  • Bookmarklets must degrade gracefully if external scripts fail
  • Bookmarklet use cases: scroll, toggle, annotate, load diagnostics
  • Only and are interactive
  • All media must include controls
  • All media must include title and aria-label
  • Use for captions and descriptions
  • No keyboard navigation or ARIA roles outside media
  • No focus management outside media
  • Greasemonkey

    RFC 5147

    // ==UserScript==
    // @name         Plaintext TOC with RFC5147
    // @namespace    http://example.com/
    // @version      1.0
    // @description  Adds an interactive table of contents to text/plain docs using RFC 5147 line fragments
    // @match        *://*/*.txt
    // @grant        none
    // ==/UserScript==
    
    (function() {
        'use strict';
    
        // Only run if the document is plain text
        if (document.contentType !== "text/plain") return;
    
        // Wrap each line in a span with data-line attribute
        const pre = document.body.querySelector("pre") || document.body;
        const lines = pre.textContent.split("\n");
        pre.innerHTML = ""; // clear
    
        lines.forEach((line, idx) => {
            const span = document.createElement("span");
            span.textContent = line + "\n";
            span.dataset.line = idx + 1; // RFC5147 line numbers start at 1
            pre.appendChild(span);
        });
    
        // Build TOC container
        const toc = document.createElement("div");
        toc.style.position = "fixed";
        toc.style.top = "10px";
        toc.style.right = "10px";
        toc.style.background = "#fff";
        toc.style.border = "1px solid #ccc";
        toc.style.padding = "10px";
        toc.style.maxHeight = "90%";
        toc.style.overflowY = "auto";
        toc.style.zIndex = "9999";
        toc.innerHTML = "<strong>Table of Contents</strong><br/>";
    
        // Simple heading detection: lines starting with digit or '#'
        lines.forEach((line, idx) => {
            if (/^(#|\d+\.)/.test(line.trim())) {
                const a = document.createElement("a");
                a.href = "#line=" + (idx + 1);
                a.textContent = line.trim();
                a.style.display = "block";
                a.style.margin = "2px 0";
                a.addEventListener("click", function(e) {
                    e.preventDefault();
                    const target = pre.querySelector(`[data-line='${idx+1}']`);
                    if (target) target.scrollIntoView({behavior:"smooth"});
                });
                toc.appendChild(a);
            }
        });
    
        document.body.appendChild(toc);
    
        // Handle RFC5147 fragments in URL
        function handleFragment() {
            const frag = location.hash;
            const match = frag.match(/line=(\d+)/);
            if (match) {
                const lineNum = parseInt(match[1], 10);
                const target = pre.querySelector(`[data-line='${lineNum}']`);
                if (target) target.scrollIntoView({behavior:"smooth"});
            }
        }
    
        window.addEventListener("hashchange", handleFragment);
        handleFragment(); // initial load
    })();
    

    Rewrite script tag content

    // ==UserScript==
    // @name         Rewrite break/var order with console.assert
    // @namespace    http://example.com/
    // @version      1.1
    // @description  Ensure var comes before break in show_alert() and assert correctness
    // @match        *://*/*
    // @grant        none
    // ==/UserScript==
    
    /*
    Example HTML this script expects:
    
    <!DOCTYPE html>
    <html>
    <head>
      <script id="foo">
    // Source - https://stackoverflow.com/a
    // Posted by Paul Sweatte, modified by community. See post 'Timeline' for change history
    // Retrieved 2025-12-12, License - CC BY-SA 3.0
    
    function show_alert()
      {
      label:
        {
        break label;
        var foo = 1;
        }
      console.log(foo);
      }
      </script>
    </head>
    <body>
    </body>
    </html>
    */
    
    (function() {
        'use strict';
    
        const oldScript = document.getElementById("foo");
        if (!oldScript) return;
    
        let code = oldScript.textContent;
    
        // Regex to capture the label block with break and var
        const regex = /label:\s*{([\s\S]*?)}/m;
        const match = code.match(regex);
    
        if (match) {
            let block = match[1];
            const breakPos = block.indexOf("break");
            const varPos = block.indexOf("var");
    
            if (breakPos !== -1 && varPos !== -1 && varPos > breakPos) {
                // Extract the var line
                const varLineMatch = block.match(/var\s+.*;/);
                let varLine = varLineMatch ? varLineMatch[0] : "";
    
                // Remove var from its old position
                let newBlock = block.replace(varLine, "");
    
                // Place var before break
                newBlock = newBlock.replace("break label;", varLine + "\n    break label;");
    
                // Replace in code
                code = code.replace(regex, "label:{\n" + newBlock + "\n}");
            }
        }
    
        // Create new script with id="bar"
        const newScript = document.createElement("script");
        newScript.id = "bar";
        newScript.textContent = code + `
    
        // Inline assertion using console.assert
        try {
            console.log("Running inline assertion for show_alert...");
            show_alert();
            console.assert(typeof foo !== "undefined" && foo === 1,
                           "Assertion failed: foo should be accessible and equal to 1");
            console.log("Assertion check complete.");
        } catch (e) {
            console.error("Assertion failed with error:", e);
        }
        `;
        document.head.appendChild(newScript);
    
        // Remove old script
        oldScript.remove();
    })();
    

    Dependency Injection for Smalltalk Developers

    • In Smalltalk, everything is an object and dependencies are messages.
    • Constructor injection feels like sending an initialize: message with the right collaborators.
    • Setter injection is simply another message you can send later, but beware of objects that don’t yet know how to respond.
    • Field injection is like directly poking into an instance variable — convenient, but it breaks the spirit of message passing.
    • Service injectors resemble a global Dictionary of services; they work, but they hide the true message protocol.
    • The @inject decorator is just syntactic sugar for declaring what messages an object expects.

    The insight is simple: keep dependencies visible in the protocol. In Smalltalk, honesty means declaring collaborators through messages, not hiding them in globals.

    Memoization

    Use memoization at the function level to avoid a decoupled caching experience which depends on the web server or a caching middleware layer

⚠️ **GitHub.com Fallback** ⚠️