Introduction to JavaScript: The Language of the Web
Why JavaScript
Brendan Eich wrote JavaScript in ten days in May 1995. Netscape needed a scripting language for their browser. It had to look like Java (marketing), act like Scheme (Eich’s preference), and be simple enough for non-programmers. The result was a language with first-class functions from Scheme, prototypal inheritance from Self, syntax from Java, and a collection of design mistakes that would haunt the web for decades.
None of that mattered. JavaScript became the only language that runs natively in every web browser on every device on Earth. There was no alternative and no competition. If you wanted to make a web page interactive, you used JavaScript. When Node.js arrived in 2009, JavaScript escaped the browser and colonized the server. Today, JavaScript is the most-used language on GitHub, the foundation of every web application, and the runtime behind billions of daily interactions.
“Any application that can be written in JavaScript, will eventually be written in JavaScript.”
I. Hello, World
In the browser (open your browser’s developer console with F12):
In Node.js (save as hello.js):
Same code runs in both environments. The browser provides the DOM (document manipulation). Node.js provides the filesystem, network, and OS access. The core language is identical.
II. Variables and Types
Primitive Types
JavaScript has one number type: 64-bit IEEE 754 floating point. There are no integers. 1 and 1.0 are the same value. This means you lose integer precision above $2^{53}$ (about 9 quadrillion). For larger integers, use BigInt.
Equality
Use === always. Pretend == does not exist. This single rule eliminates an entire category of JavaScript bugs.
III. Strings and Template Literals
IV. Objects
Objects are JavaScript’s fundamental compound type. They are key-value maps where keys are strings (or Symbols) and values are anything.
V. Arrays
Arrays in JavaScript are objects with numeric keys and a length property. Their method toolkit is extraordinary.
Learn map, filter, and reduce. These three methods replace most for loops and produce cleaner, more declarative code. Chain them together and you can express complex data transformations in a single readable expression.
VI. Functions
Closures
A closure is a function that remembers the variables from the scope in which it was created, even after that scope has closed. This is JavaScript’s most powerful feature.
Closures are how JavaScript achieves encapsulation without classes. The inner functions (increment, decrement, getCount) “close over” the count variable. They retain access to it even after createCounter has returned. React hooks, Redux reducers, Express middleware, and most JavaScript patterns are built on closures.
VII. Control Flow
Optional chaining (?.) and nullish coalescing (??) were added in ES2020 and immediately became essential. ?. prevents the “Cannot read property of undefined” error that plagues JavaScript code. ?? is a better || for defaults because it only triggers on null/undefined, not on 0, "", or false.
VIII. Prototypes and this
Every JavaScript object has a hidden link to another object called its prototype. When you access a property that does not exist on the object, JavaScript looks up the prototype chain until it finds it or reaches null.
What this Actually Means
| Context | What this Is |
|---|---|
Method call: obj.method() | obj |
Function call: fn() | globalThis (or undefined in strict mode) |
| Arrow function | Inherited from the enclosing lexical scope |
Constructor: new Foo() | The newly created object |
fn.call(ctx) / fn.bind(ctx) | ctx (explicitly set) |
The rule is simple once you learn it: this is determined by how a function is called, not where it is defined. Arrow functions are the exception: they capture this from where they are defined. When in doubt, use arrow functions.
IX. Classes
ES6 classes are syntactic sugar over prototypes. They do not introduce a new object model; they make the existing one easier to use.
Inheritance
X. Promises and Async/Await
JavaScript is single-threaded. It handles concurrency through an event loop and asynchronous callbacks. Promises and async/await make asynchronous code readable.
Promises
Async/Await
async/await is the best syntax for asynchronous programming in any mainstream language. What was nested callbacks (“callback hell”) in 2012 is clean, linear, sequential-looking code today. Under the hood it is still Promises and the event loop. The syntax just makes it readable.
XI. The Event Loop
JavaScript runs on a single thread. There are no mutexes, no deadlocks, no race conditions on shared memory. Instead, JavaScript uses an event loop: a queue of tasks that are executed one at a time.
The output order is: 1, 4, 3, 2. Why?
"1"runs immediately (synchronous)setTimeoutschedules"2"in the task queue (macrotask)Promise.thenschedules"3"in the microtask queue"4"runs immediately (synchronous)- The call stack is empty. Microtasks run first:
"3" - Then the next macrotask:
"2"
The mental model: synchronous code runs first, microtasks (Promises) run next, macrotasks (setTimeout, I/O callbacks) run last. This is why a setTimeout(fn, 0) does not run immediately — it runs after all synchronous code and all microtasks have finished.
XII. Modules
XIII. Error Handling
XIV. The DOM
The DOM is the browser’s API for interacting with HTML. Even if you use React or Vue, they call these APIs under the hood.
XV. The Ecosystem
npm has over two million packages. The node_modules directory is infamous for its size. This is a real problem, though tools like pnpm mitigate it with hard links and deduplication.
TypeScript
TypeScript is JavaScript with static types. It compiles to plain JavaScript. It has become the default for serious projects.
TypeScript catches bugs at compile time that JavaScript catches at runtime (or never). Every major framework now recommends TypeScript. If you are writing JavaScript professionally, you should be writing TypeScript.
XVI. Putting It Together
A complete Node.js program demonstrating async/await, closures, array methods, destructuring, and error handling: a concurrent URL fetcher with rate limiting.
JavaScript’s Quirks
| Expression | Result | Why |
|---|---|---|
[] + [] | "" | Both arrays coerce to empty strings, then concatenate |
[] + {} | "[object Object]" | Array → "", Object → "[object Object]", then string concatenation |
NaN !== NaN | true | IEEE 754 spec. Use Number.isNaN() |
0.1 + 0.2 !== 0.3 | true | IEEE 754 floats. Result is 0.30000000000000004. Affects every language with floats. |
typeof null | "object" | A bug from 1995. Never fixed for backwards compatibility. |
These quirks are real, documented, and make for entertaining conference talks. They rarely matter in practice. Use ===, avoid implicit coercion, use Number.isNaN(), and you will never hit them in production.
Summary
JavaScript is not a perfect language. It was designed in ten days and carries the scars. But it has genuine strengths:
- Ubiquity. It runs in every browser, on every server with Node.js, on mobile (React Native), on the desktop (Electron). No other language has this reach.
- First-class functions and closures. Taken from Scheme, these are the foundation of everything good in JavaScript: callbacks, higher-order functions, module patterns, React components.
- The event loop. A simple concurrency model. No threads, no mutexes, no deadlocks. Just callbacks and a queue.
- Async/await. The cleanest syntax for asynchronous programming in any mainstream language.
- The ecosystem. npm has more packages than any other registry. Whatever you need, someone has built a library for it.
- TypeScript. The safety net JavaScript always needed. Static types without sacrificing flexibility.
JavaScript conquered the world not because it was the best language, but because it was there — in every browser, with no installation required. And then, year by year, it got better: let/const, arrow functions, destructuring, modules, async/await, optional chaining, private class fields. The JavaScript of today is dramatically better than the JavaScript of 2005.
If you understand closures, the prototype chain, the event loop, and Promises, you understand JavaScript. Everything else — React, Node, TypeScript, every framework of the year — is built on those foundations.