Syntax
JavaScript’s syntax is C-family at the surface — curly braces, semicolons (admitted but rarely required since automatic semicolon insertion — ASI — admits omission in most cases), parentheses around if and while conditions. The conventional contemporary form is ES2015+ (also called ES6+) — admits let and const (block-scoped, replacing legacy var), arrow functions (=>), template literals (backticks), destructuring, spread/rest (...), modules (import/export), and classes. The combination — C-family core syntax, the substantial ES2015+ additions, the ASI mechanism, the conventional 'use strict' directive — is the substance of JavaScript’s syntactic identity.
This page covers the surface a working web developer encounters routinely.
A complete script
The classical hello world:
console.log("Hello, world!");
A more substantial example:
import { fetchUser } from "./api.js";
async function greet(userId) {
try {
const user = await fetchUser(userId);
const greeting = `Hello, ${user.name}!`;
document.getElementById("greeting").textContent = greeting;
} catch (err) {
console.error("Failed to greet:", err);
}
}
document.getElementById("login-button").addEventListener("click", () => {
const userId = document.getElementById("user-id").value;
greet(userId);
});
The principal features visible:
import { ... } from "..."— ES module import.async function ... { await ... }— async/await.try { ... } catch (err) { ... }— exception handling.const/let— modern variable declarations.- Template literal —
`Hello, ${user.name}!`. - Arrow function —
() => { ... }.
Source character set
JavaScript source is interpreted as UTF-16 by default; modern tooling typically uses UTF-8 source files. Identifiers admit Unicode letters; the conventional code uses ASCII identifiers.
Identifiers and naming conventions
The conventional JavaScript naming follows community style:
| Form | Use | Example |
|---|---|---|
camelCase | variables, functions, methods | userName, fetchData |
PascalCase | classes, constructors | User, EventEmitter |
UPPER_SNAKE_CASE | true constants | MAX_RETRIES |
_leadingUnderscore | conventional “private” (not enforced) | _internalState |
#field | actually private (ES2022+) | #balance |
Reserved keywords
The principal reserved words:
break case catch class const
continue debugger default delete do
else export extends false finally
for function if import in
instanceof new null return super
switch this throw true try
typeof var void while with
yield
// Strict mode reserved:
let static implements interface package
private protected public
// Reserved for future:
enum
Comments
Two comment forms:
// A single-line comment, terminated by the end of the line.
/* A block comment.
Block comments do NOT nest.
/* This is just a literal */
*/
/**
* JSDoc — admits substantial documentation tooling.
*
* @param {string} name - The user's name.
* @returns {string} The greeting.
*/
function greet(name) {
return `Hello, ${name}`;
}
The /** ... */ (JSDoc) form admits substantial documentation processing — many tools (TypeScript inference, IDE tooling, generated docs) consume JSDoc.
Statement terminators
JavaScript admits Automatic Semicolon Insertion (ASI) — newlines may terminate statements:
const x = 5
const y = 10
const z = x + y
// Equivalent:
const x = 5; const y = 10; const z = x + y;
The conventional discipline:
- With semicolons — explicit, avoids ASI pitfalls.
- Without semicolons — fewer tokens, leans on ASI; requires substantial care for some pitfalls.
The principal ASI pitfall:
const a = b
[c, d].forEach(...) // parsed as: const a = b[c, d].forEach(...)
// With semicolon:
const a = b;
[c, d].forEach(...); // safe
// Without semicolons (requires leading semicolon on lines starting with [, (, /, +, -):
const a = b
;[c, d].forEach(...) // safe
The conventional contemporary discipline (with linters like Prettier, ESLint) handles this automatically.
var, let, const
Three principal declarations:
var x = 5; // function-scoped (legacy)
let y = 10; // block-scoped, mutable
const z = 15; // block-scoped, immutable binding
The principal differences:
var | let | const | |
|---|---|---|---|
| Scope | Function | Block | Block |
| Hoisting | Yes (with undefined) | TDZ | TDZ |
| Reassignable | Yes | Yes | No |
| Redeclarable | Yes | No | No |
The conventional contemporary discipline:
- Default to
const— admit immutable bindings. - Use
letwhen reassignment is required. - Avoid
var— function-scoping admits substantial pitfalls.
// Block scoping with let/const:
{
const x = 5;
// x visible here
}
// x NOT visible here
// var hoisting pitfall:
console.log(x); // undefined (hoisted)
var x = 5;
// let TDZ:
console.log(y); // ReferenceError
let y = 5;
const admits immutable binding — the binding cannot be reassigned. Object contents may still mutate:
const obj = { count: 0 };
obj.count = 1; // OK; mutating contents
obj = {}; // ERROR; cannot reassign
Block syntax
Control-flow constructs admit braces (or single statement):
if (condition) {
doSomething();
}
if (condition) doSomething(); // OK; one-liner
while (!done) {
advance();
}
for (let i = 0; i < 10; i++) {
process(i);
}
The conventional discipline always uses braces — admit substantial readability.
Functions
Three principal forms:
// Function declaration:
function add(a, b) {
return a + b;
}
// Function expression:
const add = function(a, b) {
return a + b;
};
// Arrow function:
const add = (a, b) => a + b;
const add = (a, b) => {
return a + b;
};
const square = n => n * n; // single param: parens optional
const noArgs = () => 42; // no params: parens required
The principal differences (function vs arrow):
thisbinding — functions have ownthis; arrows inherit lexicalthis.arguments— functions havearguments; arrows do not.new— functions are constructible; arrows are not.- Hoisting — function declarations hoisted; arrows not.
The conventional contemporary discipline:
- Arrow functions for short callbacks and methods that need lexical
this. - Function declarations for top-level named functions.
Treated in Functions and closures.
Object literals
const point = {
x: 10,
y: 20,
distance() {
return Math.sqrt(this.x ** 2 + this.y ** 2);
}
};
// Shorthand properties:
const x = 10, y = 20;
const point = { x, y }; // equivalent to { x: x, y: y }
// Computed keys:
const key = "name";
const obj = { [key]: "Alice" }; // { name: "Alice" }
// Spread:
const base = { a: 1, b: 2 };
const extended = { ...base, c: 3 }; // { a: 1, b: 2, c: 3 }
Arrays
const arr = [1, 2, 3, 4, 5];
// Spread:
const more = [...arr, 6, 7]; // [1, 2, 3, 4, 5, 6, 7]
const combined = [...arr1, ...arr2];
// Rest in destructuring:
const [first, ...rest] = arr; // first=1, rest=[2,3,4,5]
// Trailing comma admitted:
const items = [
"apple",
"banana",
"cherry", // trailing comma OK
];
Destructuring
Substantial in modern JavaScript:
// Object destructuring:
const { name, age } = user;
const { name: userName, age: userAge } = user; // rename
const { name = "anonymous", age = 0 } = user; // defaults
// Array destructuring:
const [first, second] = arr;
const [head, ...tail] = arr;
const [, , third] = arr; // skip
// Nested:
const { user: { profile: { name } } } = data;
// In function params:
function greet({ name, age }) {
console.log(`${name} is ${age}`);
}
// With defaults:
function fetch(url, { method = "GET", headers = {} } = {}) {
// ...
}
Template literals
Backtick-delimited strings admit interpolation and multi-line:
const name = "Alice";
const age = 30;
const greeting = `Hello, ${name}!`;
const description = `${name} is ${age} years old`;
const multiline = `
First line
Second line
Third line
`;
// Tagged templates:
const html = String.raw`<div>${name}</div>`;
Treated in Strings.
Spread and rest
The ... admits spread (in calls and literals) and rest (in destructuring):
// Spread:
const arr = [1, 2, 3];
console.log(Math.max(...arr)); // 3 (spread as args)
const more = [...arr, 4]; // array spread
const obj2 = { ...obj1, x: 10 }; // object spread
// Rest:
function sum(...nums) {
return nums.reduce((a, b) => a + b, 0);
}
const [first, ...rest] = arr;
const { name, ...others } = user;
'use strict'
The directive enables stricter parsing and error handling:
'use strict';
x = 5; // ERROR: x is not declared
ES modules and class bodies are strict by default. The conventional contemporary discipline uses ES modules — admit substantial implicit strict mode.
Modules
ES modules (the conventional contemporary form):
// math.js
export function add(a, b) { return a + b; }
export const PI = 3.14159;
export default function multiply(a, b) { return a * b; }
// main.js
import multiply, { add, PI } from "./math.js";
console.log(add(2, 3));
console.log(multiply(2, 3));
In HTML:
<script type="module" src="main.js"></script>
Treated in Scope and modules.
Classes
ES2015+ class syntax (sugar over prototypes):
class Point {
#x; // private field
#y;
constructor(x, y) {
this.#x = x;
this.#y = y;
}
get x() { return this.#x; }
get y() { return this.#y; }
distance(other) {
return Math.sqrt((this.#x - other.x) ** 2 + (this.#y - other.y) ** 2);
}
static origin() {
return new Point(0, 0);
}
}
class ColoredPoint extends Point {
constructor(x, y, color) {
super(x, y);
this.color = color;
}
}
Treated in Classes and prototypes.
A note on what JavaScript admits
Several distinguishing features:
- Dynamic typing — types attach to values, not variables.
- Prototype-based inheritance — every object admits a hidden link to another.
- First-class functions — functions are values.
- Closures — substantial, ubiquitous.
- Garbage collection — automatic memory management.
- Single-threaded — event-loop concurrency model.
- Async/await — admits substantial sequential-style async code.
- Object literal — substantial, with substantial shorthand syntax.
- Truthy/falsy coercion —
0,"",null,undefined,NaN,falseare falsy. - Two equality operators —
==(with coercion) and===(strict). - No integer type (until BigInt) — all numbers were Number (IEEE 754 double).
A note on what is not in JavaScript
- No static types (until TypeScript) — admit substantial runtime errors.
- No threading in the language — Web Workers admit substantial parallelism.
- No native
print()—console.logis the conventional substitute. - No standard file I/O in browsers — File API and Fetch are the substitutes.
- No traditional namespaces — modules and objects are the conventional substitute.
- No native immutability —
Object.freezeadmits shallow freeze.
The combination — dynamic typing, prototype-based objects, first-class functions, single-threaded event-loop concurrency, ES2015+ syntactic richness, the substantial Web platform integration — is the substance of JavaScript’s identity. The discipline of writing modern JavaScript is largely about using the contemporary ES2015+ features (const, arrow functions, destructuring, modules, classes, async/await) and avoiding the legacy pitfalls (var, ==, accidental globals).