# JavaScript and TypeScript

# Library

# Utilities

# Libraries

Name Description
Ramda A practical functional library
Project Fluent A localization system for natural-sounding translations
Anime A lightweight animation library

# Throttle, Debounce

function throttle(func: Function, delay: number): (...rest: any[]) => any {
    let start = performance.now();

    return function (...args: any[]): any {
        if (performance.now() - start > delay) {
            start = performance.now();

            return func.call(this, ...args);
        }
    };
}
function debounce(func: Function, delay: number): (...rest: any[]) => void {
    let timer: number;

    return function (...args: any[]) {
        clearTimeout(timer);
        timer = setTimeout(func.bind(this, ...args), delay);
    };
}

# Generate an unique ID

function uid(): string {
    return Math.random()
        .toString(36)
        .substr(2, 9);
}

# User Interface

# Frameworks

Name Description
Vue The Progressive JavaScript Framework
Svelte Cybernetically enhanced web apps

Vue and Svelte, both, are very performant and use Single File Component that allows to have scoped styles, unlike React. The Vue ecosystem is fabulous. Use Vue3 and the Composition API.

CSS-in-JS is bullshit. Please use CSS modules.

# Components

Mostly Framework agnostic.

Name Description
Tippy The complete tooltip, popover, dropdown, and menu solution

# Bundlers / Tools

Name Description
Rollup Rollup is a module bundler
Vite (Vue) Vite is an web dev build tool that serves your code via native ES Module
Snowpack / Pika The build tool for modern web apps
Parcel Zero configuration web application bundler
Name Description Language
esbuild An extremely fast JavaScript bundler and minifier Go
deno A secure runtime for JavaScript and TypeScript Rust

# Promises, Async and Await [TODO]

# Proxy

The Proxy object is used to define custom behavior for fundamental operations. It can be seen as the dot "." on object notation.

# Example: Web Storage API

Here is an example to transfom the Web Storage API as if it is a single Object, or a key/value DB. It's a unusual usage of Proxy to show you the power of this API. This could be improved by using JSON parse() or stringify().

export const storage = getProxy(localStorage);
export const session = getProxy(sessionStorage);

function getProxy(webStorage: Storage): object {
    return new Proxy(
        {}, // this can be an inner object to update
        {
            set(_obj, prop, value) {
                webStorage.setItem(prop.toString(), String(value));
                return true;
            },
            get(_obj, prop) {
                webStorage.getItem(prop.toString());
            },
            has(obj, prop) {
                return typeof Reflect.get(obj, prop) === 'string';
            },
            deleteProperty(_obj, prop) {
                webStorage.removeItem(prop.toString())
                return true;
            },
        },
    );
}

# Usage

// storage => localStorage | session => sessionStorage
import { storage, session } from "./proxy-storage.ts";

storage.foo = "bar"; // *Storage.setItem("foo", "bar");
storage.foo // => "bar" | *Storage.getItem("foo");
storage.foo ? "yes" : "no"; // => "yes" | no *Storage equivalent
delete storage.foo; // *Storage.removeItem("foo");
// test Storage support
function hasStorageSupport(webStorage: Storage): boolean {
    try {
        webStorage.setItem('__storage__', 'foo');
        webStorage.removeItem('__storage__');
        return true;
    } catch (_e) {
        return false;
    }
}

# Tagged Template literals [TODO]

# Web Workers

The Web Workers doesn't have access to the DOM API. The cost needed for the communication between threads may be high. Try to use the main thread for UI changes only, at least, as often as possible.

# Simple Example

let element = document.querySelector("#result");
let worker = new Worker("./worker.js");

worker.addEventListener("message", event => {
    let sum = event.data;

    element.innerHTML = sum;
})

worker.postMessage({a: 2, b: 3});

# worker.js

let ctx = self;

self.addEventListener("message", event => {
    let {a, b} = event.data;
    let sum = a + b;

    ctx.postMessage(sum);
});

# Libraries

# Image manipulation

As a Worker doesn't have access to the DOM, in order to transfer ImageData to a Worker, you need to decode the image in the main thread:

export function getImageData(src: string, size?: number): Promise<ImageData> {
    return new Promise((resolve, reject) => {
        let image = new Image();
        let canvas = document.createElement("canvas");
        let context = canvas.getContext("2d");

        image.addEventListener("error", reject);
        image.addEventListener("load", () => {
            let width = image.naturalWidth;
            let height = image.naturalHeight;
            let finalWidth = size || width;
            let finalHeight = size ? size / (width / height) : height;

            canvas.width = finalWidth;
            canvas.height = finalHeight;

            context.drawImage(image, 0, 0, width, height, 0, 0, finalWidth, finalHeight);

            let data = context.getImageData(0, 0, finalWidth, finalHeight);

            resolve(data);
        });

        image.src = src;
    });
}