Where it runs
JavaScript runs inside an engine. In a browser that engine is given the DOM, the network, storage and the screen. In Node it is given the file system, processes and sockets instead. The language is the same; what it can reach is not.
Everything in this course runs in a browser unless it says otherwise.
Loading a script
type="module"is the default choice now: deferred, strict mode, module scope,importandexport.deferdownloads in parallel and runs after parsing, in document order.asyncruns as soon as it arrives, in no order. Only for independent third-party scripts.- A plain inline
<script>in the head runs before the body exists —document.querySelector("h1")will benull.
<!-- a module: deferred automatically, has its own scope, can import -->
<script type="module" src="/app.js"></script>
<!-- a classic script that waits for the HTML to be parsed -->
<script defer src="/app.js"></script>
<!-- inline: runs immediately, blocks parsing -->
<script>
console.log("hello");
</script>
The console is not just for logging
console.log("plain");
console.table([{ id: 1, name: "Painted" }, { id: 2, name: "Veneer" }]);
console.group("Fitting"); console.log("step 1"); console.groupEnd();
console.time("render"); /* … */ console.timeEnd("render");
console.count("clicked");
console.assert(total > 0, "total should be positive");
console.error("something went wrong"); // includes a stack trace
console.dir(element); // the object, not the rendered element
console.log({ total, items }) prints {total: 5, items: Array(3)} — the braces give you the variable names for free, which beats console.log(total) twice over.
Statements and semicolons
A statement is one instruction. Semicolons end them, and JavaScript will insert them for you if you leave them out — usually correctly, and occasionally not. Pick one style and let a formatter enforce it; the arguments about this are not worth your time.
const a = 1
const b = 2
// this line starts with a bracket, so it is read as a[...] — an error
const c = a
[b].forEach(x => x)
// a leading semicolon, or just keeping them everywhere, avoids it
;[b].forEach(x => x)
Comments
Write comments about why, not what. The code already says what it does; it cannot say what you rejected, or which bug this line is working around.
// to the end of the line
/* over
several lines */
/**
* A JSDoc block. Editors read these for autocompletion.
* @param {number} mm - the opening width in millimetres
* @returns {string}
*/
function label(mm) {
return mm + " mm";
}
Strict mode
Modules and class bodies are always in strict mode. It turns silent failures into errors — assigning to an undeclared variable throws instead of creating a global, and duplicate parameter names are rejected. There is no reason to write non-strict code today.
"use strict"; // first line of the file or the function
Your first program
Backticks make a template literal: ${…} inserts a value, and the string may span lines. It is the default way to build a string in modern JavaScript.
const doors = [
{ name: "Painted", days: 5 },
{ name: "Veneer", days: 10 },
{ name: "Ceramic", days: 15 },
];
const quickest = doors.reduce((best, d) => (d.days < best.days ? d : best));
console.log(`Quickest: ${quickest.name}, ${quickest.days} days`);
console.table(doors);
When something goes wrong
- Read the error.
Uncaught TypeError: Cannot read properties of null (reading "addEventListener")means aquerySelectorreturnednull— the element was not there when the script ran. - Click the file:line in the error to jump to the source.
- Add
debugger;to a line and the browser stops there with everything in scope visible. - The Sources panel lets you set a breakpoint by clicking a line number — no editing required.