# JavaScript

## What it is

JavaScript is a dynamically typed programming language, standardised as ECMAScript, that every web
browser can execute and that also runs on servers and build machines through runtimes such as
Node.js. People shorten it to JS. The standard's yearly editions are named ES2015, ES2024 and so on,
the older numbering (ES5, ES6) is still heard, and "vanilla JavaScript" means the language with no
framework on top. Apart from the name and some curly-brace syntax, it shares little with Java.

Brendan Eich wrote the first version at Netscape, and it shipped in the Navigator 2.0 browser. Ecma
International adopted the first edition of the standard, ECMA-262, in June 1997, and its committee
TC39 has published a new edition every year since ECMAScript 2016. A proposal joins the
specification only at stage 4 of the committee's process, which requires implementations that
already ship, so the text and the engines seldom disagree for long.

The language says very little about the outside world. ECMA-262 defines the syntax, the types,
objects, promises and modules, and leaves input and output to whatever hosts it. In a browser that
environment is the web platform: the document, events, timers and network requests, specified mostly
by the HTML Standard and its sibling standards. Node.js supplies files, sockets and processes
instead. That split explains a common surprise: code can be perfectly valid JavaScript and still
break when it moves from a page to a server, because the language travelled and its environment
stayed behind.

In a browser, a page's scripts share one main thread with layout, painting and input. A task runs to
completion before the browser can respond to the next click. Promises and `async` functions keep
waiting off that thread, while computation stays on it unless it is moved to a Web Worker. Numbers
are IEEE 754 double-precision values, so whole numbers are exact only up to 2^53 − 1, and anything
larger needs `BigInt`.

## Why it matters

The real decision is how much of it to send to the people reading your site. JavaScript is the
language that makes a page behave without a round trip to a server, and every kilobyte of it is
downloaded, parsed, compiled and run on a device you do not control. The bundle that feels instant
on a developer's laptop can hold a mid-range phone busy for seconds, and the reader pays for that in
battery and patience.

Script is also where a page is most exposed. Anything injected into it runs with the page's full
authority: it can read what the reader types and send it anywhere the page may send. That is the
risk Content Security Policy exists to contain, and a page with no script offers nothing to inject
into. The package ecosystem adds a second exposure. A typical front-end build pulls in hundreds of
transitive dependencies, and each one is code that executes with the same rights as yours.

The other side of the ledger is real. Running one language on both sides of a system lets the
browser and the server share a validation rule or a data shape, and some products need a lot of
script: offline state, drag and drop, maps, live collaboration. The useful question is asked per
feature: does this need to happen in the reader's browser without asking a server? If it does not,
render it on the server or at build time and ship HTML.

The related choice is JavaScript vs TypeScript. TypeScript adds a type checker and a build step and
compiles back to plain JavaScript. For a codebase several people change every week, the checker
usually pays for itself; for a hundred-line enhancement on a static page, JavaScript and a strict
linter are enough.

## What it looks like in practice

This site ships almost no JavaScript to its readers and runs a great deal of it at build time.
Three pages show both halves of that.

The public site is a static build in three languages with no runtime backend, and the browser
receives a single first-party script.
[That script is a scoped exception](/portfolio/built-a-multilingual-static-site-in-hugo-126/)
for one feature, a portfolio filter whose controls stay hidden until the script has run. The rest of
the site, 108 template files including 53 partials and 18 shortcodes, does its work when the site
is built. The page records the cost of that choice as well: anything interactive must be solved at
build time, which is why a search index was costed and parked rather than shipped.

The same content is published over Gemini and Gopher, two protocols with no place for a script at
all.
[One build writes 777 documents in each format](/portfolio/mirrored-the-site-to-gemini-and-gopher-137/)
from the same source as the pages, and a measurement confirmed that the HTML did not change by a
single byte.
The two alternates add about twelve per cent to the HTML's weight on disk. The site argues that a
document does not need a megabyte of script to be read, and a format that cannot hold one is the
plainest way to show it.

Most of the JavaScript here lives in the tooling. The site's quality gate is thirty-two scripts and
10,242 lines of JavaScript, the largest body of code in the repository, and for a while it was the
one body of code that nothing formatted, linted or type-checked.
[Bringing it under a formatter and a linter](/portfolio/brought-the-quality-gate-code-under-a-linter-143/)
began by fitting the formatter to the code: the repository's four-space default would have rewritten
2,173 lines of the largest checker, and a two-space override for those files left 38 lines of real
drift. The linter then raised 13 findings, and all 13 were fixed with none suppressed. A bug in a
checker shows itself as a check that passes when it should fail, so the gate's own code gets the
treatment it gives everything else.

## Where it goes wrong

The failures below follow from how the language is built, and each has a known remedy.

**The page renders, then ignores taps for a second or more.** A long task, such as hydrating a large
bundle, parsing a big JSON response or sorting thousands of rows, holds the one thread that handles
input. Ship less code for the first interaction by splitting bundles per route and deferring the
rest, break long work into pieces that yield back to the event loop, and move heavy computation into
a Web Worker. Measure on a slow phone rather than on the laptop that wrote the code.

**Prices and identifiers come out slightly wrong.** Every number is a binary floating-point value,
so decimal fractions round and very large integers collapse into their neighbours. A 64-bit database
key sent as a JSON number is the usual victim:

```js
0.1 + 0.2 === 0.3; // false
Number.MAX_SAFE_INTEGER; // 9007199254740991
9007199254740993 === 9007199254740992; // true
```

Keep money in integer minor units such as cents, send large identifiers as strings, and use
`BigInt` where the arithmetic has to be exact.

**A comparison lets the wrong value through.** The `==` operator converts its operands before
comparing them, so `"" == 0` and `"0" == false` are both true. Use `===` throughout and let the
linter's `eqeqeq` rule enforce it, so the conversion is something the code asks for out loud.

**Text from a user ends up running as code.** Assigning untrusted text to `innerHTML`, building
markup by string concatenation or handing input to `eval` turns data into script. Write text with
`textContent`, send a Content Security Policy that admits scripts only by nonce or hash, and keep
the dependency list short and pinned in a lockfile, reading what each update pulls in.

**Code that works in the browser crashes on the server.** The language moved and its environment did
not: `window` and `document` exist only in a browser, and Node.js reads a file as CommonJS or as an
ES module depending on settings the browser never sees. Keep runtime-specific calls at the edges,
set `"type": "module"` explicitly in `package.json`, and run each piece of code in the environment
it will actually run in.

## Further reading

- [ECMAScript Language Specification](https://tc39.es/ecma262/) — the living draft of ECMA-262,
  the text every engine implements, updated as proposals reach stage 4.
- [The TC39 Process](https://tc39.es/process-document/) — the stages a proposal passes through
  before it becomes part of the language, and what each one demands.
- [ECMA-262 at Ecma International](https://ecma-international.org/publications-and-standards/standards/ecma-262/)
  — the published yearly editions, for when a project has to name the version it targets.
- [HTML Standard: event loops](https://html.spec.whatwg.org/multipage/webappapis.html#event-loops)
  — how a browser schedules tasks, microtasks and rendering around a page's scripts.
- [HTML Standard: the script element](https://html.spec.whatwg.org/multipage/scripting.html#the-script-element)
  — classic and module scripts, and how `async` and `defer` change when they run.

<https://engineer.company/glossary/javascript/>
