JavaScript Language Overview


  • Description: What JavaScript is, where it runs, the ECMAScript versions, strict mode, source structure (scripts vs modules), and the relationship to HTML/CSS in the browser.
  • My Notion Note ID: K2A-F4-1
  • Created: 2018-03-23
  • Updated: 2026-05-17
  • License: Reuse is very welcome. Please credit Yu Zhang and link back to the original on yuzhang.io

Table of Contents


1. What JavaScript Is

JavaScript is a dynamically typed, lexically scoped, single-threaded, multi-paradigm language standardised as ECMAScript by TC39. Multi-paradigm means imperative, object-oriented (prototype-based), and functional features all coexist.

In a browser, JavaScript is the behaviour layer:

Layer Language
Content / structure HTML
Presentation CSS
Behaviour / interactivity JavaScript

The name dates to 1995 (Netscape, originally LiveScript). It is unrelated to Java despite the name. The standardised dialect is ECMAScript (ES); "JavaScript" colloquially means ES + the browser/Node API surfaces.

2. Where JavaScript Runs

Host Engine Runtime APIs
Chrome / Edge / Opera V8 DOM, BOM, Web APIs
Firefox SpiderMonkey DOM, BOM, Web APIs
Safari JavaScriptCore DOM, BOM, Web APIs
Node.js V8 Filesystem, network, child processes, modules
Deno V8 + Rust runtime Web-platform-compatible APIs + permissions
Bun JavaScriptCore + Zig Node-compatible APIs, faster startup
Cloudflare Workers / Vercel Edge V8 isolates Subset of Web APIs (Fetch, Streams, KV)
Embedded (e.g. QuickJS) Various Whatever the host provides

The engine parses and executes JS; the runtime provides the host-specific APIs. Same engine, different runtimes — V8 in Chrome exposes document, V8 in Node exposes fs.

3. ECMAScript Versions

Year Edition Notable additions
1997 ES1 Initial standard.
1999 ES3 try/catch, regex literals. Long the browser baseline.
2009 ES5 strict mode, JSON, Object.create, getters/setters, array iteration (forEach, map).
2015 ES6 / ES2015 let, const, arrow functions, classes, modules, template literals, Promise, Map/Set, destructuring, spread/rest, default params, for…of. The biggest jump in years.
2016 ES2016 **, Array.prototype.includes.
2017 ES2017 async/await, Object.entries/values.
2018 ES2018 Rest/spread for objects, async iterators, regex improvements.
2019 ES2019 Array.flat, Object.fromEntries, optional catch binding.
2020 ES2020 Optional chaining ?., nullish coalescing ??, BigInt, dynamic import(), globalThis.
2021 ES2021 ??=, `
2022 ES2022 Class fields, private methods, top-level await, Object.hasOwn, Array.at.
2023 ES2023 Array.findLast, toSorted/toReversed (non-mutating).
2024 ES2024 Promise.withResolvers, RegExp v flag, Object.groupBy.
2025 ES2025 Iterator helpers (map/filter/take on iterators), Promise.try, sync Set methods.

Since 2015 the spec ships annually with whatever proposals reached Stage 4. Compatibility tables: https://compat-table.github.io/compat-table/es2016plus/.

4. Embedding in HTML

<!-- External — preferred. Cached, smaller HTML, easier to maintain. -->
<script src="/app.js"></script>

<!-- Inline -->
<script>
  console.log('Hello');
</script>

<!-- ES module — opt-in, deferred by default -->
<script type="module" src="/main.js"></script>

<!-- Skip execution in browsers that lack module support -->
<script nomodule src="/legacy.js"></script>

Attributes that change when the script runs:

Attribute Behaviour
(none) Block parsing, fetch, execute. Slowest.
defer Fetch in parallel, execute after HTML parsing in document order. Use for scripts that need the DOM ready.
async Fetch in parallel, execute as soon as fetched. Order not guaranteed. Use for independent analytics/ads.
type="module" Implicitly deferred. Plus module semantics.

Rule of thumb: external <script defer src=…> in <head> for almost everything. Modules in <head> are fine too — they defer by default.

5. Scripts vs Modules

Two different parse modes; differences matter daily.

Feature Classic script ES module (type="module" or .mjs)
import/export No Yes
Top-level await No Yes
this at top level globalThis (browser: window) undefined
Strict mode Opt-in via "use strict" Always on
Variables in top-level scope Polluting global Module-scoped only
Loading Synchronous unless defer/async Deferred + executed once even if imported twice
CORS Cross-origin allowed without CORS headers (legacy "no-cors" load — response is opaque to JS) Cross-origin requires CORS headers (Access-Control-Allow-Origin)

In Node:

  • .js files inherit module type from package.json "type" field ("module" or "commonjs").
  • .mjs forces ESM, .cjs forces CommonJS, regardless of package.json.

6. Strict Mode

'use strict';   // first statement in a file or function

Strict mode tightens the language:

  • Assignment to an undeclared variable throws instead of creating a global.
  • Duplicate parameter names throw at parse time. (Duplicate object literal keys were a strict-mode error in ES5 but became legal again in ES2015 — needed for computed keys.)
  • this is undefined in plain functions called without an object (not coerced to globalThis).
  • delete on a non-configurable property throws.
  • with statement banned.
  • eval runs in its own scope; can't inject vars into the surrounding scope.

ES modules are always strict. Class bodies are always strict. New code rarely benefits from non-strict mode.

7. Syntax Essentials

7.1 Statements and Semicolons

const x = 1
const y = 2

console.log(x + y)

Semicolons are optional thanks to Automatic Semicolon Insertion (ASI). Two pitfalls where ASI doesn't insert and you need explicit semicolons:

const a = b + c
;[1, 2, 3].forEach(...)    // leading [ would otherwise be treated as index access

const f = () => {}
;(() => { /* IIFE */ })()  // leading ( would call f

Codebase convention is what matters. Either always include semicolons (Airbnb style) or always omit and prefix the few risky lines with ; (Prettier's "no-semi" style). Don't mix.

7.2 Variable Declarations

let count = 0;        // block-scoped, reassignable
const PI = 3.14;       // block-scoped, not reassignable
var x = 1;             // function-scoped, hoisted, legacy
Keyword Scope Reassignable Hoisted
const Block No Yes (TDZ until declaration)
let Block Yes Yes (TDZ until declaration)
var Function or global Yes Yes, initialised to undefined

TDZ (Temporal Dead Zone) — for let/const, the variable exists from the start of the block but throws ReferenceError until the declaration line executes:

console.log(x);   // ReferenceError
let x = 5;

var is the historical declaration. New code should use let/const exclusively.

Prefer const. Use let only when you need to reassign. Reassignment is rarer than people think — const obj = {}; obj.x = 1; is fine because we're mutating, not reassigning.

7.3 Identifiers

  • Start with letter, _, or $; can include digits after.
  • Case-sensitive (nameName).
  • Unicode allowed (const π = 3.14;).
  • Reserved words can't be used as identifiers (for, class, return, etc.).

8. Comments and Style

// Single-line comment

/*
 * Multi-line comment.
 */

/**
 * JSDoc comment — semantic for tooling.
 * @param {string} name
 * @returns {string}
 */
function greet(name) {
  return `Hello, ${name}`;
}

JSDoc annotations power IDE tooltips and TypeScript type checking on .js files (// @ts-check).

Conventions (informal but widely used):

  • camelCase for variables and functions.
  • PascalCase for classes and constructors.
  • SCREAMING_SNAKE_CASE for compile-time-ish constants.
  • _leadingUnderscore for "intended-private" (no language-level effect; classes have # for real private fields).
  • Two-space indentation (most communities). Four spaces in some legacy codebases.

Tooling:

  • ESLint — finds bugs and enforces style. Configurable rules.
  • Prettier — opinionated formatter. Drop-in, almost no config.
  • TypeScript — adds static types. Worth adopting for anything beyond a script.

9. References