#lang scribble/manual @(require scribble/core (for-label racket/base "../main.rkt")) @(define (side-by-side racket-source js-source) (tabular #:style 'boxed #:sep (hspace 2) (list (list (bold "Racket / js-maker") (bold "Generated JavaScript")) (list (verbatim racket-source) (verbatim js-source))))) @(define (tested s) (nested #:style 'inset (bold "Tested behavior: ") s)) @title{jsmaker JavaScript Use Cases} @author+email["Hans Dijkema" ""] @defmodule[jsmaker/demo/js-usecases] This document describes the practical JavaScript use cases in @filepath{demo/js-usecases.rkt}. Each implementation is written as a Racket snippet using @racket[js] and is tested by compiling it to JavaScript and executing that JavaScript with the configured test executor. The corresponding tests are in @filepath{testing/jsmaker-usecases.rkt}. The tests intentionally use @racket[js/expression] for their calls wherever possible. Raw JavaScript remains only in small harness preambles, such as fake @tt{setInterval}, fake DOM objects, and fake @tt{fetch}. @section{Running the examples} Generate the JavaScript examples with: @codeblock{ racket demo/js-usecases.rkt } Run the use-case regression tests with: @codeblock{ racket testing/jsmaker-usecases.rkt } @section{Use cases} @subsection{1. Random number between 1 and 5} @(side-by-side #<string age)))))) RKT #< { const { name: name, age: age = 0 } = person; return (name + ":" + String(age)); })(); } JS ) @(tested "The object { name: \"Ada\", age: 37 } is rendered as \"Ada:37\".") @subsection{6. Escaping a timer interval} @(side-by-side #< { const { a: a3 } = obj; obj.b = 2; obj["c"] = 3; delete obj["a"]; return [a1, a2, a3, obj.b, obj["c"], Object.hasOwn(obj, "a")]; })(); } JS ) @(tested "The three reads produce 1, the two writes produce 2 and 3, and the deleted property is absent.") @subsection{8. String concatenation order} @(side-by-side #< n 1) (let* ([i 1]) (while (< i n) (when (> (list-ref a (- i 1)) (list-ref a i)) (let* ([tmp (list-ref a (- i 1))]) (vector-set! a (- i 1) (list-ref a i)) (vector-set! a i tmp))) (set! i (+ i 1)))) (set! n (- n 1))) (return a)))) RKT #< 1)) { let i = 1; while ((i < n)) { if ((a[(i - 1)] > a[i])) { let tmp = a[(i - 1)]; a[(i - 1)] = a[i]; a[i] = tmp; } i = (i + 1); } n = (n - 1); } return a; } JS ) @(tested "Sorting [5, 1, 4, 2, 8] yields [1, 2, 4, 5, 8].") @subsection{15. Recursive binary search} @(side-by-side #< low high) (return -1) (let* ([mid (send Math floor (/ (+ low high) 2))] [value (list-ref xs mid)]) (cond [(= value target) (return mid)] [(< value target) (return (binarySearch xs target (+ mid 1) high))] [else (return (binarySearch xs target low (- mid 1)))]))))) RKT #< high)) return -1; let mid = Math.floor(((low + high) / 2)); let value = xs[mid]; if ((value === target)) return mid; if ((value < target)) return binarySearch(xs, target, (mid + 1), high); return binarySearch(xs, target, low, (mid - 1)); } JS ) @(tested "Searching 7 returns index 3; searching 4 returns -1.") @subsection{16. Count occurrences with Map} @(side-by-side #<