Mateen Kiani
Published on Tue Jun 24 2025·5 min read
Are you tired of wondering why a variable you defined in one file suddenly pops up in another? We talk a lot about functions, objects, and modern syntax, but the sneaky world of global variables often slips under the radar. Why do some variables end up attached to the browser’s window or Node’s global object without you realizing it?
It all comes down to understanding how JavaScript handles variable declarations and scope. Once you grasp these rules, you’ll spot issues early, write cleaner code, and avoid those surprise bugs that haunt your projects.
JavaScript has three main scopes: global, function, and block. Global scope means a variable is accessible anywhere in your code. Function scope limits access to inside a function. Block scope, introduced in ES6, confines let and const to their {}
block.
When you declare with var
outside any function, it becomes global. Inside functions, var
is scoped to that function. But let
and const
never leak out of their block.
var a = 1; // global or function-scopedlet b = 2; // block-scopedconst c = 3; // block-scopedif (true) {var x = 'hi'; // global if outside functionlet y = 'hey'; // only here}console.log(x); // 'hi'console.log(y); // ReferenceError
By choosing the right declaration keyword, you decide where your data lives. Keep this rule in mind to avoid accidental globals.
If you assign a value to an undeclared variable, JavaScript quietly creates a global. This happens in sloppy mode when you forget var
, let
, or const
.
function greet() {message = 'hello'; // no var/let/const}greet();console.log(message); // 'hello' - global!
This mistake can cause hard-to-trace bugs, especially in large codebases. A tiny typo like useData
instead of userData
might create a new global and break logic elsewhere.
Tip: Always use strict mode (
"use strict";
) at the top of your files. It prevents implicit globals and throws an error instead.
Linting tools like ESLint can catch these slips. Set up rules such as no-undef
and no-global-assign
to enforce declarations and keep your global footprint zero.
In browsers, the global object is window
. In Node.js, it's called global
. Variables declared globally become properties of these objects.
// Browserwindow.foo = 'bar';console.log(foo); // 'bar'// Nodeglobal.count = 0;console.log(global.count); // 0
When you say var name = 'Alice';
at the top level, it’s equivalent to window.name = 'Alice';
. This means libraries or other scripts might overwrite your data.
In Node, each module has its own scope, so top-level var
doesn’t leak to global
. But if you do global.myVar = 5
, it’s shared across modules.
Need to make HTTP calls and wonder about globals in Node? Check out Node HTTP Requests.
Minimizing globals leads to safer, modular code. Follow these steps:
let
and const
for all variables. Avoid var
unless you need function-scoped behavior."use strict";
) to catch undeclared assignments.Tip: Group related functions and data into modules. This single change can reduce accidental global clashes.
By following these practices, you keep your code predictable and maintainable.
ES6 modules bring true file-level scope. When you import or export, nothing leaks into the global object.
Declaration Type | Scope |
---|---|
var | Function or global |
let / const | Block |
import | Module (file) |
export | Module (file) |
// math.jsexport function add(a, b) {return a + b;}// app.jsimport { add } from './math.js';console.log(add(2, 3)); // 5
Since add
is not on window
or global
, you avoid name collisions. Modules also enable tree-shaking in bundlers, so unused code doesn’t bloat your bundle.
Frameworks often need shared state. In React, you might be tempted to stick data on window
or global. Instead, use context or state management libraries. For instance, React’s CreateContext API lets you provide and consume data without globals.
import React, { createContext, useContext } from 'react';const AuthContext = createContext();function AuthProvider({ children }) {const [user, setUser] = useState(null);return (<AuthContext.Provider value={{ user, setUser }}>{children}</AuthContext.Provider>);}function Profile() {const { user } = useContext(AuthContext);return <div>{user ? user.name : 'Guest'}</div>;}
Read more about this pattern in the React CreateContext guide. It keeps your data flow clear and avoids hidden globals.
Catching globals early saves hours of debugging. Combine these tools:
no-undef
, no-unused-vars
, and no-global-assign
rules.Tip: Automate linting in your CI pipeline. That way, no code with accidental globals ever reaches production.
Global variables in JavaScript come from how you declare them and where. Implicit globals sneak in when you forget let
, const
, or var
. Browsers expose globals on window
; Node uses global
but modules wrap your code by default. Modern ES6 modules and patterns like React context keep your state local and predictable.
By using strict mode, linting, and modules, you avoid namespace pollution. You’ll write code that’s easier to test, debug, and maintain. So next time you spot a global, take a moment to ask: "Is this meant to be shared everywhere?" If the answer is no, scope it down.
JavaScript variables declared without let
or const
in global scope become properties of the global object (window or global).