Implicit Globals — How One Missing 'let' Breaks Sessions
A missing 'let' in a function created an implicit global that swapped user sessions.
20+ years shipping production JavaScript and front-end systems at scale. Lessons pulled from things that broke in production.
- JavaScript is the programming language of the web, running in every browser.
- No setup needed — open DevTools, start coding in the Console immediately.
- Variables (
let,const) store data; functions group reusable logic. - The DOM is JavaScript's live map of HTML — use
getElementByIdandaddEventListenerto make pages interactive. - Watch out for type coercion:
"5" + 3gives"53", not8. Convert strings to numbers withNumber(). - This combination of client-side execution, dynamic typing, and DOM access is why JavaScript dominates web development.
JavaScript is the programming language that makes web pages interactive. Unlike HTML (structure) and CSS (presentation), JavaScript runs in the browser to respond to clicks, validate forms, animate elements, and fetch data from servers. It's also used outside browsers—Node.js runs JavaScript on servers, and frameworks like React, Vue, and Angular power modern web apps.
The core language is standardized as ECMAScript, with ES6 (2015) introducing let, const, arrow functions, and classes that fundamentally changed how you write safe, predictable code.
Variables are how JavaScript remembers data—numbers, strings, booleans, arrays, objects. The critical distinction is between var, let, and const. var has function scope and can be redeclared, leading to bugs. let and const are block-scoped and prevent redeclaration. const means the binding can't change, but the value (like an object's properties) can still mutate.
The 'implicit global' trap: if you assign to a variable without any declaration keyword (x = 5), JavaScript creates a property on the global object (window in browsers, global in Node). This leaks state across functions and modules—one missing let can corrupt session data, overwrite other scripts' variables, or cause silent failures that are hell to debug.
Functions are reusable blocks of code. You define them with function keyword, arrow syntax (=>), or as methods on objects. They can take parameters, return values, and close over variables (closures). The DOM (Document Object Model) is the browser's API for manipulating HTML—document.getElementById, element.textContent, element.style.
Events are how users interact: clicks, keypresses, form submissions. You attach event listeners with addEventListener, and the callback runs when the event fires. The implicit global problem bites here: if your event handler accidentally assigns to an undeclared variable, it pollutes the global scope, potentially breaking other handlers or session state.
Always use strict mode ('use strict') to catch these—it throws errors for implicit globals, silent failures, and other legacy footguns.
Think of a website like a puppet show. HTML is the wooden puppet — it gives everything its shape and structure. CSS is the costume and paint — it makes the puppet look beautiful. JavaScript is the puppeteer's hands — it's the force that actually makes things move, react, and come alive. Without JavaScript, every website would be a static poster on a wall. With it, buttons click, forms validate, menus slide open, and games run — all inside your browser.
Every time you like a post on Instagram, get a live search suggestion on Google, or watch a video auto-play on YouTube, JavaScript is running behind the scenes. It's not an exaggeration to say JavaScript is the most widely deployed programming language in human history — it runs in every single web browser on the planet, and increasingly on servers, mobile apps, and even smart TVs. If you want to build anything on the web, JavaScript isn't optional. It's the language of the web.
Before JavaScript existed (it was created in just 10 days in 1995 by a developer named Brendan Eich), websites could only display fixed content. If you filled in a form wrong, the entire page had to reload just to tell you the email field was empty. JavaScript solved that problem by giving browsers a built-in programming language — one that could react to what users do, change content on the fly, and communicate with servers without ever refreshing the page. It turned the web from a library of documents into a platform for full applications.
By the end of this article you'll understand exactly what JavaScript is and where it lives, how to write and run your first JavaScript program, what variables, data types, and functions are, and how JavaScript actually makes a web page respond to a user. You won't just memorise syntax — you'll understand the thinking behind it, so every concept sticks.
What JavaScript Actually Is — and Where It Lives
JavaScript is a programming language, which just means it's a set of instructions you write that a computer can understand and execute. What makes JavaScript unique is WHERE it runs: inside the browser. Chrome, Firefox, Safari, and Edge all have a JavaScript engine built right in. Chrome uses one called V8, Firefox uses SpiderMonkey. These engines read your JavaScript code and run it instantly, right on the user's machine — no installation needed, no compilation step, no waiting.
This is called a 'client-side' language, because the code runs on the client (the user's computer) rather than on a server somewhere else. That's what makes JavaScript feel so fast and reactive — when you type in a search box and suggestions appear instantly, no request went to a server. JavaScript handled everything locally in milliseconds.
JavaScript also has a second home now: Node.js, created in 2009, lets JavaScript run on servers too. So the same language you use to make a button change colour can also power a full backend API. That's a huge deal — it means you can become a full-stack developer knowing just one language. But for now, let's focus on JavaScript in the browser, which is where every beginner should start.
// This is a JavaScript comment — the browser ignores anything after // // Comments are notes for humans, not instructions for the computer // console.log() prints a message to the browser's developer console // Think of it as JavaScript's way of talking back to you while you're building console.log("Hello! JavaScript is running."); // You can print numbers too — no quotes needed for numbers console.log(42); // You can even do maths right inside console.log // JavaScript evaluates the expression first, then prints the result console.log(10 + 5); // Prints 15, not the string "10 + 5" // Let's make it personal let visitorName = "Alex"; // We're storing the name "Alex" in a labelled box called visitorName console.log("Welcome to the site, " + visitorName + "!");
Variables and Data Types — Teaching JavaScript to Remember Things
A variable is just a labelled storage box. You put a value in the box, give the box a name, and then use that name whenever you need the value again. If the value changes later, you update the box — you don't rewrite your entire program.
In modern JavaScript there are two keywords for creating variables: let and const. Use let when the value will change over time (like a score in a game). Use const when the value should never change (like the number of days in a week). There's also an older keyword var — you'll see it in older code, but avoid it in new code because it has confusing behaviour we'll cover in the gotchas.
JavaScript handles several types of data. A string is text (always wrapped in quotes). A number is any numeric value — integers and decimals use the same type. A boolean is simply true or false, like a light switch. null means 'intentionally empty'. undefined means a variable was declared but never given a value. Understanding the difference between null and undefined trips up a lot of beginners — null is a deliberate empty value you set, undefined is JavaScript saying 'you never told me what this is'.
// --- STRINGS: any text, wrapped in quotes --- let playerName = "Jordan"; // double quotes work let welcomeMessage = 'Hello there'; // single quotes also work let greeting = `Hi, ${playerName}!`; // backticks let you embed variables directly — called a template literal console.log(greeting); // Prints: Hi, Jordan! // --- NUMBERS: integers and decimals, same type --- let playerScore = 0; // starts at zero let itemPrice = 4.99; // decimals are fine let maxLives = 3; console.log(playerScore + 10); // Prints: 10 — JavaScript adds the number 10 to 0 // --- BOOLEANS: only ever true or false --- let isLoggedIn = false; // user hasn't signed in yet let hasWonGame = false; console.log(isLoggedIn); // Prints: false // --- CONST: a value that should never be reassigned --- const DAYS_IN_A_WEEK = 7; // convention: uppercase names for true constants const SITE_NAME = "TheCodeForge"; console.log(`Welcome to ${SITE_NAME}. You have ${DAYS_IN_A_WEEK} days to complete the challenge.`); // --- NULL vs UNDEFINED --- let selectedItem = null; // we intentionally set this to 'nothing' — the user hasn't picked yet let shippingAddress; // declared but no value given — JavaScript makes this undefined automatically console.log(selectedItem); // Prints: null console.log(shippingAddress); // Prints: undefined
"5" + 3 gives you "53", NOT 8. When you use + with a string and a number, JavaScript glues them together as text instead of doing maths. This is called type coercion. Always make sure you're working with the right type before doing arithmetic. Use parseInt("5") or Number("5") to convert a string to a real number first."5" and adding it to 3 gives "53" instead of 8.Number() or parseInt().const by default, let when you need reassignment.var in new code — its loose scoping causes bugs.letconstvarvar for minimal changes, but migrate to let/const when possiblevar — always const by default, let when you need reassignmentFunctions — Writing Instructions Once, Using Them Anywhere
A function is a named, reusable block of instructions. Think of it like a recipe card. You write the recipe once — 'to make a grilled cheese: get bread, add cheese, apply heat' — and then any time anyone wants a grilled cheese, you just say 'make grilled cheese' instead of re-explaining every step. Functions work exactly the same way in code.
You define a function using the function keyword, give it a name, and put the instructions inside curly braces {}. A function can accept inputs called parameters (the ingredients) and can send back a result using return (the finished dish). Calling a function means actually running it — you do that by writing the function's name followed by parentheses.
Functions are one of the most powerful ideas in all of programming because they let you avoid repeating yourself. If you need to change how something works, you change it in ONE place — the function — and every part of your program that calls it gets the update automatically. The alternative, copy-pasting the same logic in 20 places, is a maintenance nightmare that every experienced developer has painful memories of.
// --- DEFINING a function --- // 'function' keyword, then a name, then () for inputs, then {} for the instructions function greetUser(userName) { // userName is a parameter — a placeholder for whatever name gets passed in let message = `Hey ${userName}, welcome back!`; return message; // return sends the result back to wherever the function was called } // --- CALLING the function --- // We pass in a real value (called an argument) and store the returned result let greetingForSarah = greetUser("Sarah"); let greetingForMarcus = greetUser("Marcus"); console.log(greetingForSarah); // Prints: Hey Sarah, welcome back! console.log(greetingForMarcus); // Prints: Hey Marcus, welcome back! // The function ran twice, with different inputs each time — that's the power of reuse // --- A FUNCTION THAT DOES MATHS --- function calculateDiscountedPrice(originalPrice, discountPercent) { let discountAmount = (originalPrice * discountPercent) / 100; let finalPrice = originalPrice - discountAmount; return finalPrice; } let shirtPrice = calculateDiscountedPrice(50, 20); // 20% off £50 let laptopPrice = calculateDiscountedPrice(1200, 15); // 15% off £1200 console.log(`Shirt after discount: £${shirtPrice}`); // Prints: Shirt after discount: £40 console.log(`Laptop after discount: £${laptopPrice}`); // Prints: Laptop after discount: £1020 // --- ARROW FUNCTION (modern shorthand, same idea) --- // For simple one-line functions, developers often use this shorter syntax const doubleTheScore = (score) => score * 2; console.log(doubleTheScore(75)); // Prints: 150
function greetUser(userName), userName is the parameter. In greetUser("Sarah"), "Sarah" is the argument. Getting this right shows you understand how data flows through a program.this binding (e.g., inside an object method).map() or .filter())function declaration — it's hoistedMaking Web Pages React — JavaScript in the Browser with the DOM
So far we've written JavaScript that just talks to the console. But the whole point of JavaScript on the web is making pages react to users. To do that, JavaScript uses the DOM — the Document Object Model. Don't let the jargon scare you. The DOM is simply JavaScript's map of your HTML page.
When a browser loads your HTML, it reads every element — every heading, button, paragraph, image — and builds a tree-shaped map of them in memory. JavaScript can then reach into that map, find any element, change its text, change its style, or tell it to listen for user actions like clicks and keyboard presses.
You find an element using document.getElementById() or document.querySelector(). Once you have the element, you can change what it displays with .textContent or .innerHTML. You make it respond to a click by adding an event listener — essentially saying 'when this button is clicked, run this function'. This is the core loop of ALL interactive web development: find an element, listen for an action, change something in response.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>JavaScript DOM Example</title> <style> body { font-family: sans-serif; padding: 40px; } #score-display { font-size: 2rem; font-weight: bold; color: #2c3e50; } button { padding: 12px 24px; font-size: 1rem; cursor: pointer; background: #3498db; color: white; border: none; border-radius: 6px; } button:hover { background: #2980b9; } </style> </head> <body> <h1>Click Counter</h1> <!-- This paragraph shows the score. Its id lets JavaScript find it. --> <p id="score-display">Score: 0</p> <!-- When this button is clicked, the addPoint function will run --> <button id="add-point-button">+ Add Point</button> <script> // Step 1: Find the elements we want to work with // getElementById searches the DOM for an element with this exact id const scoreDisplay = document.getElementById("score-display"); const addPointButton = document.getElementById("add-point-button"); // Step 2: Set up our data let currentScore = 0; // this variable tracks the score between clicks // Step 3: Define what happens when the button is clicked function addPoint() { currentScore = currentScore + 1; // increase the score by 1 // Step 4: Update the page to show the new score // textContent changes what text the element displays scoreDisplay.textContent = `Score: ${currentScore}`; // Bonus: change colour at certain milestones if (currentScore >= 10) { scoreDisplay.style.color = "#e74c3c"; // red at 10+ } } // Step 5: Attach the function to the button's 'click' event // 'addEventListener' says: whenever THIS element fires THIS event, run THIS function addPointButton.addEventListener("click", addPoint); </script> </body> </html>
<script> tag is at the bottom of the <body>, not in the <head>. This matters. If your script runs before the HTML elements exist in the DOM, getElementById will return null and your code will crash. Putting the script at the bottom guarantees the HTML is loaded first. Alternatively, use defer in your script tag: <script src="app.js" defer> — this achieves the same effect even with an external file.getElementById call.defer attribute on external scripts.getElementById to find elements, then textContent to change them.addEventListener — it's the universal pattern for all user-driven behaviour..textContent — it's safe and avoids XSS risks.innerHTML — but be careful with user supplied data to prevent XSS.style.property = value.addEventListener(eventType, handlerFunction)Events and Event Listeners — Making Your Page Respond to Users
You've seen how to update the DOM when a button gets clicked. But events are much broader than clicks. Every user action fires an event: moving the mouse, pressing a key, submitting a form, resizing the window, even the page finishing loading. JavaScript can listen for any of them.
The key method is addEventListener. It takes two arguments: the event type (like 'click', 'keydown', 'submit') and a function that runs when the event occurs. The function receives an 'event object' that carries details — which key was pressed, which element was clicked, the mouse coordinates. You can access it through a parameter in your handler.
Event delegation is a technique where you attach ONE listener to a parent element instead of many listeners to individual children. It works because events 'bubble up' through the DOM tree. This is essential when you have dynamic lists or tables with hundreds of cells — attaching a listener to each cell kills performance.
<!DOCTYPE html> <html> <body> <input type="text" id="name-input" placeholder="Type your name..."> <p id="output">Hello, ???</p> <script> // Listen for keyboard input in real time const nameInput = document.getElementById('name-input'); const output = document.getElementById('output'); // The 'input' event fires on every keystroke nameInput.addEventListener('input', function(event) { // event.target is the element that received the event const currentText = event.target.value; // the text currently in the input output.textContent = `Hello, ${currentText || '???'}!`; }); // ----- Event Delegation Example ----- // Without delegation we'd attach a listener to each <li> individually // With delegation we attach ONE listener to the parent <ul> const list = document.getElementById('item-list'); list.addEventListener('click', function(event) { // Check if the clicked element is an <li> if (event.target.tagName === 'LI') { console.log('You clicked:', event.target.textContent); event.target.style.backgroundColor = '#d1fae5'; // highlight } }); </script> <ul id="item-list"> <li>Apple</li> <li>Banana</li> <li>Cherry</li> </ul> </body> </html>
- Event starts at the deepest element (target) and fires its handler.
- Then it moves up to the parent, then grandparent, all the way to the document.
- You can stop this bubble with
event.stopPropagation()— useful when a child's action shouldn't trigger the parent's handler. - If you attach a listener to a parent, you catch events from all children — that's delegation.
addEventListener to listen, not onclick in HTML — it keeps logic and markup separate.addEventListener directly to that element<ul> or containerevent.preventDefault() inside your handlerkeydown event listener to the document or a specific inputStop Asking Where to Download JavaScript — It’s Already in Your Browser
Every week, someone searches "download JavaScript." Stop. JavaScript is not a standalone app you install. It’s the built-in scripting engine of every web browser. Chrome uses V8. Firefox uses SpiderMonkey. Safari uses JavaScriptCore. These engines parse and execute your code the instant a page loads. You don’t pay a cent. You don’t download anything. To write JavaScript, you only need a text editor and a browser. Open DevTools (F12), click the Console tab, and type console.log("Hello, browser"). That’s it. You just ran JavaScript. The moment you understand this, you stop wasting time searching for downloads and start coding. This accessibility is what makes JavaScript the most democratic programming language on the planet. No setup. No cost. Just a browser and your brain.
// No installation needed. Open any browser's DevTools console. console.log("Hello, browser"); console.log("JavaScript runs here, right now."); console.log("No downloads. No setup. Just code.");
The Only Way to Learn JavaScript Is by Typing (Not Just Reading)
Reading a tutorial and nodding along builds false confidence. Real learning happens when your fingers hit the keyboard and your code breaks. JavaScript is especially brutal here — silent type coercions, undefined is not a function, and the dreaded NaN. Every example in every tutorial expects you to type it, run it, and break it. That’s why the best tutorials embed a "Try it Yourself" editor. It forces you to engage. Don’t copy-paste. Type each line. Change the variable name. Add a console.log where none exists. Break the code on purpose. Then fix it. This cycle of break-and-fix builds muscle memory faster than any passive reading marathon. The W3Schools examples are deliberately simple. Use them as a starting point, not the finish line. After you run their example, modify it. That’s when the language becomes yours.
// Step 1: Run this exactly as written. let score = 100; if (score = 50) { console.log("Score changed to 50"); } // Step 2: Notice the bug (assignment vs equality). // Step 3: Fix it to '===' and run again.
=== always. It catches bugs before they bite you in production. Linters (like ESLint) will flag accidental assignments in conditions. Use them from day one.Global Variable Leak: The Silent Production Bug
score = 0 inside a function instead of let score = 0, creating an implicit global variable. That global score accidentally matched a session ID variable initialised elsewhere, overwriting the user's identifier.'use strict'; at the top of all scripts — strict mode throws an error for undeclared variable assignments. Or simply always use let/const.- Always declare variables with
letorconst— never rely on implicit globals. - Enable strict mode on every JavaScript file to catch accidental globals immediately.
- Use a linter like ESLint with the
no-undefrule — it flags undeclared variables at build time.
console.log('test'); at the very top to confirm the engine is running.let/const? Is it defined inside another function and you're trying to access it outside? Use console.log(typeof x) before the line that errors to see if it exists.getElementById on an element that doesn't exist yet. Move your <script> tag to just before </body> or wrap code in a DOMContentLoaded event listener.addEventListener is attached after the button element exists in the DOM. Also verify the event type string — it should be 'click', not 'onclick'.Check Network tab for the script file — expected 200, not 404.If inline, look for red errors in Console; if none, add a `debugger;` statement and reload.Check scope — is the variable inside a function and you're trying to access it outside?Look for typos in variable names; JavaScript is case-sensitive.let and assign an initial value; if it comes from a function, make sure the function returns the value.| Feature | let | const | var |
|---|---|---|---|
| Can be reassigned? | Yes — value can change | No — value is locked after assignment | Yes — value can change |
| Scope (where it lives) | Block-scoped — only exists inside its {} block | Block-scoped — only exists inside its {} block | Function-scoped — leaks out of if/for blocks, causes bugs |
| Hoisted? (available before declaration) | No — causes ReferenceError if used too early | No — causes ReferenceError if used too early | Yes — but the value is undefined until assigned (confusing) |
| Use it when... | The value needs to update (counters, user input) | The value is fixed (config, constants, DOM references) | Avoid in new code — only appears in legacy codebases |
| Example | let score = 0; score = 10; | const MAX_RETRIES = 3; | var oldStyle = true; (don't use) |
Key takeaways
const by default, switch to let only when you need to reassignvar in new code. This alone prevents a whole class of bugs.querySelector finds elements, textContent changes what they display, and addEventListener makes them react to usersaddEventListener for separation of concerns, and event delegation for handling many similar elements efficiently.Common mistakes to avoid
3 patternsUsing = instead of === for comparison
if (userAge = 18) always passes because it assigns 18 to userAge instead of comparing. The condition evaluates to 18 (truthy), so the branch always runs — no syntax error, but logic is wrong.=== (strict equality) for comparisons. It checks both value and type. If you need loose comparison, understand it's almost never the right choice. Lint rules like no-cond-assign catch this automatically.Calling a function before the DOM has loaded
document.getElementById('myButton').addEventListener(...) at the top of a script in the <head> returns null because the button element doesn't exist yet. The error reads 'Cannot read properties of null'.<script> tag to just before </body>, or use the defer attribute on external scripts. Alternatively, wrap code in a DOMContentLoaded event listener.Assuming console.log() pauses execution
console.log(result) thinking it will stop the program so they can inspect the value, then get confused when code continues and produces an error. console.log just prints and moves on.console.log only for output — it doesn't halt execution. To pause and inspect, use debugger; statement or set a breakpoint in the Sources tab of DevTools.Interview Questions on This Topic
What is the difference between `null` and `undefined` in JavaScript? Can you give a real-world scenario where you'd encounter each?
undefined means a variable has been declared but never assigned a value — JavaScript sets it automatically. null is an intentional assignment meaning 'no value' or 'empty'. Example: a user's middle name field that they choose not to fill — you'd set it to null. A variable declared but not initialised, like let address;, is undefined until a value is given.Explain the difference between `let`, `const`, and `var`. Which would you use by default and why?
var has function scope and is hoisted with an initial undefined — this leads to bugs in loops and conditionals. let and const have block scope (exist only inside {}). const prevents reassignment (but not mutation of objects/arrays). Default to const because it signals intent and prevents accidental overwrites. Use let only when you know you need to reassign (e.g., loop counter). Never use var in new code.What is the DOM, and how does JavaScript interact with it? Walk me through what happens when you call `document.getElementById` and then change an element's `textContent`.
document.getElementById('myId'), JavaScript traverses that tree and returns the element node (or null if not found). Once you have the element, assigning to its .textContent property updates the text inside that node, and the browser re-renders that part of the page. This triggers no full page reload — only the affected subtree is repainted.Explain the concept of hoisting in JavaScript. How does it affect the way you write code?
var declarations are hoisted and initialised with undefined, so you can reference them before the line they're written on (the value will be undefined). let and const are hoisted but not initialised — referencing them before declaration throws a ReferenceError (Temporal Dead Zone). Function declarations are hoisted entirely, so you can call them before their definition in the file. This means you can organise code with utility functions at the bottom and logic on top, but you should still always declare variables at the top of their scope for clarity.Frequently Asked Questions
No — despite the similar name, they're completely different languages with different syntax, use cases, and ecosystems. Java is a compiled, statically-typed language used heavily in enterprise backends and Android apps. JavaScript is a dynamic, interpreted language that runs in browsers. The name similarity is a historical marketing decision from 1995 and has confused beginners ever since.
Not a thing. Every modern browser has a JavaScript engine built in. Open Chrome or Firefox, press F12 to open DevTools, click the Console tab, and you can start writing and running JavaScript immediately. When you're ready to build full web pages, a free text editor like VS Code is all you need.
HTML defines the structure of a page (headings, buttons, paragraphs), CSS controls how it looks (colours, fonts, layout), and JavaScript controls how it behaves (what happens when you click something, live data updates, animations). Learn HTML and basic CSS first — they take a weekend to grasp and they give you the 'puppet' that JavaScript will later bring to life. Trying to learn JavaScript before understanding basic HTML is like learning to drive without knowing what a steering wheel is.
== compares values after type coercion (e.g., '5' == 5 is true). === compares both value and type (e.g., '5' === 5 is false). Always prefer === to avoid subtle bugs. The only exception is when you intentionally want to treat null and undefined as equal — == sees them as equal, which can be useful but is rarely recommended.
Yes. Frameworks like React Native, Flutter (with Dart, but there are JS wrappers), and Apache Cordova let you build mobile apps using JavaScript. The code runs on the device using a JavaScript engine. Additionally, TypeScript (a superset of JavaScript) is now the primary language for React Native.
20+ years shipping production JavaScript and front-end systems at scale. Lessons pulled from things that broke in production.
That's JS Basics. Mark it forged?
7 min read · try the examples if you haven't