JavaScript has a reputation for quietly doing the wrong thing. Assign to a variable you
never declared and it happily creates a global. Call a function as a plain function and
this silently becomes the window object. Delete a variable and nothing
happens. Write a function with two parameters of the same name and the second one quietly
overwrites the first.
All of that is sloppy mode — the default behaviour of a classic script. Strict mode is the opt-in alternative, and it turns nearly every one of those silent mistakes into a loud error.
This guide covers the whole subject: what strict mode is, how to enable it at file and function level, the complete list of behaviours it changes, why ES modules and classes are strict automatically, and the mistakes it catches. Every example runs live.
"use strict"; that turns several silent mistakes into thrown errors and
removes a handful of unsafe or deprecated language features.
Sloppy Mode vs Strict Mode
JavaScript has two execution modes. The default — the one you get with no directive at all — is called sloppy mode, a name chosen deliberately. It is forgiving in ways that made sense in 1995 and cause bugs today.
Forgiving and quiet
Assigning to an undeclared name creates a global variable.
this inside a plain function call is the global object.
Duplicate parameter names are allowed; the last one wins.
Deleting a variable or function does nothing, with no error.
Writing to a read-only property fails silently.
Strict and loud
Assigning to an undeclared name throws a ReferenceError.
this inside a plain function call is undefined.
Duplicate parameter names are a syntax error.
Deleting a variable or function is a syntax error.
Writing to a read-only property throws a TypeError.
The philosophy behind strict mode is simple: a mistake that would have gone unnoticed should be a mistake that stops the program. It does not add new features — it removes dangerous ones and makes the remaining rules tighter.
Strict mode was introduced in ECMAScript 5 in 2009. It was designed so that existing code would keep working unchanged — opt-in only, no breaking changes for scripts that did not ask for it.
How to Enable Strict Mode
Strict mode is turned on by a single line — the exact string
"use strict";, including the quotes. Where you put it determines how much of
the file it affects.
Top of the file
Every line of the script runs in strict mode, including functions defined inside it.
Top of a function body
Only that function is strict. Everything outside it stays in sloppy mode.
Modules and classes
Strict by default. No directive is needed, and there is no way to opt out.
File level
"use strict";
// Everything below this line is strict.
const name = 'Ada'; // fine
undeclared = 'oops'; // ReferenceError
function greet() {
// Still strict — the directive covers the whole file
return this; // undefined, not the global object
}
Function level
// Sloppy mode out here
sloppyGlobal = 'created'; // silently creates a global
function strictFn() {
"use strict";
// Strict mode in here only
strictGlobal = 'nope'; // ReferenceError
}
function sloppyFn() {
// Back to sloppy mode
anotherGlobal = 'created'; // silently creates a global
}
Modules and classes are automatic
// module.js — no directive needed
export const value = 1;
// This file is strict automatically
undeclared = 5; // ReferenceError
// And a class body is strict too
class User {
constructor(name) {
// Strict mode inside any class
this.name = name;
}
}
<script type="module" src="app.js"></script>
Most modern code is loaded with type="module", which means strict mode is
already on. Writing the directive in a module is harmless but redundant.
The Placement Rules
The directive has one rule that trips people up constantly: it must be the very first statement in the file or function. A comment before it is fine. Anything else — even a harmless expression — makes it an ordinary string and disables strict mode entirely, with no warning.
There is no warning when the directive is misplaced. The string is a valid expression statement, so the parser accepts it without complaint and the code simply runs in sloppy mode. If strict mode appears not to be working, check the first line.
See it in action
The preview below compares the same three assignments in sloppy and strict mode. In sloppy mode, an undeclared assignment creates a global. In strict mode, it throws — and the error is caught so the page can keep running.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Strict Mode Demo</title>
<style>
body { font-family: system-ui, sans-serif; padding: 24px; background: #f8fafc; font-size: 14px; margin: 0; }
.row {
background: #fff; border: 1px solid #e2e8f0; border-radius: 10px;
padding: 12px 16px; margin-bottom: 8px;
}
.label { font-family: ui-monospace, monospace; color: #64748b; font-size: 12px; display: block; margin-bottom: 4px; }
.value { font-family: ui-monospace, monospace; font-size: 13px; font-weight: 700; }
.ok { color: #16a34a; }
.err { color: #e11d48; }
</style>
</head>
<body>
<div id="output"></div>
<script>
const out = document.getElementById('output');
function show(label, value, cls) {
const div = document.createElement('div');
div.className = 'row';
div.innerHTML =
'<span class="label">' + label + '</span>' +
'<span class="value ' + (cls || '') + '">' + value + '</span>';
out.appendChild(div);
}
// This script is sloppy mode — no directive
// 1. An undeclared assignment in sloppy mode
try {
sloppyGlobal = 'created';
show('sloppy: undeclared assignment', 'created a global — no error', 'ok');
show('window.sloppyGlobal', String(window.sloppyGlobal), 'ok');
} catch (e) {
show('sloppy: undeclared assignment', e.name, 'err');
}
// 2. A strict function inside a sloppy file
function strictFn() {
"use strict";
try {
strictGlobal = 'nope';
return 'no error — this should not happen';
} catch (e) {
return e.name + ': ' + e.message;
}
}
show('strict function: undeclared assignment', strictFn(), 'err');
// 3. this inside a plain call, in each mode
function sloppyThis() { return this; }
function strictThis() { "use strict"; return this; }
show('sloppy: this in a plain call',
sloppyThis() === window ? 'window (the global object)' : 'something else', 'ok');
show('strict: this in a plain call',
strictThis() === undefined ? 'undefined' : 'something else', 'err');
// 4. Duplicate parameters — a syntax error in strict mode
show('strict: duplicate parameter names',
'SyntaxError — cannot even be parsed', 'err');
</script>
</body>
</html>
Note the fourth row. Duplicate parameter names are not a runtime error — they are a syntax error. The function cannot be parsed at all in strict mode, which is why the example simply describes it rather than trying to run it.
What Strict Mode Changes
Strict mode changes about a dozen specific behaviours. They fall into three groups:
mistakes that become errors, changes to this, and restrictions on
eval and arguments.
Group 1 — Silent mistakes become errors
Undeclared assignment
Sloppy: silently creates a global variable.
Strict: throws ReferenceError.
Deleting a variable
Sloppy: does nothing, no error.
Strict: syntax error — the script will not parse.
Duplicate parameter names
Sloppy: the last parameter wins silently.
Strict: syntax error.
Octal literals
Sloppy: 010 means 8.
Strict: syntax error. Use 0o10 instead.
The with statement
Sloppy: allowed, and rarely used.
Strict: forbidden entirely.
Writing to read-only
Sloppy: fails silently.
Strict: throws TypeError.
Adding properties to frozen objects
Sloppy: fails silently.
Strict: throws TypeError.
Reserved words
Strict reserves implements, interface, let, package, private, protected, public, static, yield.
Group 2 — Changes to this
function whoAmI() {
return this;
}
whoAmI() === window // true
// A plain function call sets
// this to the global object.
// This is how accidental globals
// get created."use strict";
function whoAmI() {
return this;
}
whoAmI() === undefined // true
// A mistake that would have
// silently written to the global
// object now throws a TypeError.
Method calls are unaffected — obj.method() still binds this to
obj. Arrow functions still inherit this from the surrounding
scope. Only plain function calls change.
Group 3 — eval and arguments
"use strict";
// 1. eval does not leak variables into the surrounding scope
eval("var x = 5;");
// x is not defined here
// 2. You cannot assign to eval or arguments
eval = 5; // SyntaxError
arguments = 5; // SyntaxError
// 3. arguments no longer tracks parameter changes
function f(a) {
arguments[0] = 99;
return a; // still the original value
}
// In sloppy mode, changing arguments[0]
// would also change `a`.
Most modern code never uses eval or reads arguments directly.
The third group of changes is mostly relevant to legacy code, but it is worth knowing
why the behaviour differs when you encounter it.
The Complete List
Here is every change strict mode makes, in one table.
| Behaviour | Sloppy mode | Strict mode |
|---|---|---|
| Undeclared assignment | Creates a global | ReferenceError |
| Assigning to a read-only property | Fails silently | TypeError |
| Adding to a frozen object | Fails silently | TypeError |
| Deleting a variable or function | Does nothing | Syntax error |
| Duplicate parameter names | Last one wins | Syntax error |
Octal literals (010) |
Allowed | Syntax error |
The with statement |
Allowed | Syntax error |
this in a plain function call |
The global object | undefined |
Assigning to eval or arguments |
Allowed | Syntax error |
Variables declared inside eval |
Leak into the enclosing scope | Stay inside eval |
arguments tracks parameters |
Yes | No — the two are disconnected |
| Reserved words | Usable as names | Reserved: implements, interface, package, private, protected, public, static, yield |
It does not change how variables are scoped, how the event loop works, how
this behaves in method calls, or anything about performance. It is a
correctness feature, not a new execution model.
Why Modules and Classes Are Always Strict
ES modules and class bodies are strict by default. There is no directive to write and no way to opt out. This was a deliberate design decision, not an accident.
Modules
A module was a new construct with no legacy code to protect. The designers could simply make it strict, and every module written since has benefited from the stricter rules.
// module.js — strict automatically
undeclared = 5;
// ReferenceError, without any directive
function whoAmI() {
return this;
}
whoAmI();
// undefined, not the global object
Class bodies
Everything inside a class declaration or expression runs in strict mode,
including the constructor and every method.
class Counter {
#count = 0; // private fields only exist in strict mode
increment() {
// this method is strict
this.#count++;
return this.#count;
}
// A method called without a receiver has this === undefined
getValue() {
return this.#count;
}
}
const c = new Counter();
// Detaching the method loses this — and in a class
// this is undefined rather than the global object
const detached = c.getValue;
detached();
// TypeError: Cannot read private member from an object
// whose class did not declare it
// Private fields are a strict-mode-only feature, which is
// one reason classes were made strict from the start.
Private fields — the ones that start with # — only exist in strict mode.
Making class bodies strict automatically was the cleanest way to make the feature
available everywhere without a directive.
Real Bugs Strict Mode Catches
Strict mode is not theoretical. These are the specific bugs it catches in real code, all of which would otherwise slip through silently.
1. A typo in a variable name
// Sloppy mode — silently wrong
const userName = 'Ada';
userNmae = 'Grace'; // typo — creates a new global
console.log(userName); // 'Ada' — the typo had no effect
console.log(userNmae); // 'Grace' — a global that should not exist
// Strict mode — caught immediately
"use strict";
const userName2 = 'Ada';
userNmae2 = 'Grace'; // ReferenceError: userNmae2 is not defined
This is the single most valuable thing strict mode does. A typo that would have created a ghost global becomes an error on the very next line.
2. Losing this in a callback
// Sloppy mode — this silently becomes the global object
const timer = {
seconds: 0,
start: function () {
setInterval(function () {
this.seconds++; // this === window
// No error. window.seconds becomes NaN.
}, 1000);
}
};
// Strict mode — this is undefined, so the bug is loud
"use strict";
const timer2 = {
seconds: 0,
start: function () {
setInterval(function () {
this.seconds++; // TypeError: Cannot read property
// 'seconds' of undefined
}, 1000);
}
};
// The fix in both cases is an arrow function, which
// inherits this from the surrounding scope.
3. Writing to a frozen object
const config = Object.freeze({
apiUrl: 'https://api.example.com',
timeout: 5000
});
// Sloppy mode — silently fails
config.timeout = 10000; // no error, no change
// Strict mode — TypeError
"use strict";
config.timeout = 10000; // TypeError: Cannot assign to read only property
4. Duplicate parameters hiding a bug
// Sloppy mode — the second parameter silently wins
function area(width, width) {
return width * width;
}
area(5, 10); // 100 — the first argument is ignored
// Strict mode — syntax error, caught before the code runs
"use strict";
function area2(width, width) { }
// SyntaxError: Duplicate parameter name not allowed in this context
Every one of these bugs is a mistake that would have gone unnoticed until much later — in production, in a bug report, in a corrupted global. Strict mode moves the discovery to the moment the mistake is made.
Common Mistakes
These are the specific errors that come up when people first use strict mode. Each one produces a bug that is easy to diagnose once you know what to look for.
1. The directive is not first
// Some comment
const a = 1;
"use strict";
undeclared = 5; // no error — still sloppy mode
// The directive is the third statement,
// so it is a plain string expression
// and does nothing.2. An empty statement before the directive
3. Expecting strict mode to cover a whole file when only a function is marked
function first() {
"use strict";
// strict here
}
function second() {
// sloppy here
undeclared = 5; // creates a global
}
second(); // no error"use strict";
function first() {
// strict here
}
function second() {
// strict here too
undeclared = 5; // ReferenceError
}4. Confusing strict mode with TypeScript
They are unrelated. Strict mode is a runtime mode built into JavaScript itself; TypeScript is a separate language that adds a static type system and compiles to JavaScript. A TypeScript file is not automatically strict at runtime unless the emitted JavaScript includes the directive or is loaded as a module — which, in practice, it usually is.
5. Expecting strict mode to catch logic errors
"use strict";
function add(a, b) {
return a - b; // a logic bug
}
add(2, 3); // -1
// Strict mode has nothing to say about this.
// Both the arguments and the return value
// have the types the function expects.
// TypeScript would catch it; strict mode
// at runtime cannot.
6. Writing the directive in a module and expecting it to matter
// module.js
"use strict"; // redundant — modules are strict already
export const value = 1;
// Harmless, but unnecessary. If you write it,
// some linters will flag it as redundant.
There is no warning, no console message, and no lint error from the language itself when the directive is misplaced. If strict mode seems not to be working, check whether the directive is genuinely the first statement.
Best Practices
These habits keep strict mode working for you rather than quietly doing nothing.
Use ES modules
type="module" is strict automatically, with no directive to misplace.
Put the directive on line one
Or immediately after a comment. Never after any actual statement.
Prefer file-level over function-level
Mixing strict and sloppy in one file is confusing. Keep it consistent.
Use arrow functions for callbacks
They inherit this, so the detached-method bug cannot occur.
Freeze your config objects
Object.freeze() plus strict mode turns accidental writes into errors.
Let ESLint enforce it
The strict rule can require the directive, or flag it as unnecessary in modules.
Use classes for state
Class bodies are strict, and private fields only work in strict mode.
Check the source map
Bundlers sometimes move code between files. If strict mode is off, the directive may have been dropped.
Avoid eval and with
They are forbidden or restricted in strict mode, and almost never the right tool anyway.
Use ES modules and strict mode comes for free. For classic scripts, put
"use strict"; on the first line of the file.
Frequently Asked Questions
1. What is strict mode in JavaScript?
Strict mode is an opt-in mode that makes JavaScript throw errors for certain mistakes
that would otherwise pass silently. It is enabled by writing the exact string
"use strict" at the top of a file or function, and it also turns off
several deprecated or unsafe language features.
2. How do I enable strict mode?
Write "use strict"; as the first statement in a file to enable it for the
whole script, or as the first statement inside a function body to enable it for that
function only. ES modules and class bodies are strict automatically, so no directive
is needed there.
3. Is strict mode the default in JavaScript?
Not for ordinary scripts. Classic script files and plain functions run in sloppy mode
unless the directive is present. However, any file loaded with
type="module" and any code inside a class body is strict by default.
4. What does strict mode change?
It turns several silent mistakes into errors — assigning to an undeclared variable,
deleting a variable or function, duplicate parameter names, octal literals, and writing
to read-only properties. It makes this undefined inside plain function
calls instead of the global object, restricts eval and
arguments, and reserves words like implements,
interface, package, and private.
5. What does this mean inside a function in strict mode?
In sloppy mode, a plain function call sets this to the global object. In
strict mode, this is undefined. Method calls still bind
this to the object before the dot, and arrow functions still inherit
this from the surrounding scope.
6. Why does strict mode make undeclared variable assignment throw?
In sloppy mode, assigning to a name that was never declared silently creates a global
variable. Strict mode turns that into a ReferenceError, which catches a
typo in a variable name immediately rather than letting it pollute the global scope.
7. Can I use strict mode for only part of a file?
Yes. Writing "use strict"; as the first statement inside a function body
enables strict mode for that function only. Strict mode and sloppy mode can coexist in
the same file at different scopes, though mixing them is usually a sign the file should
be split.
8. Are ES modules strict by default?
Yes. A script with type="module" is always strict, and there is no way to
opt out. The same applies to the body of a class declaration, which runs in strict mode
whether or not the directive appears.
9. Does strict mode affect performance?
Modern engines optimize both modes well, so the difference is negligible in practice.
Strict mode can in principle be faster because it removes ambiguity about
this and disallows features that block optimization, but the effect is
small compared with the correctness benefit.
10. Why is it the string “use strict” and not a keyword?
Strict mode was designed so that older browsers which did not understand it would simply ignore the string as a harmless expression statement. Using a keyword would have caused a syntax error in those browsers instead.
11. Does the order of statements matter when enabling strict mode?
Yes. The directive must be the very first statement in the file or function. A comment before it is fine, but any actual statement — even a harmless one — makes the directive a plain string expression that has no effect, and the code silently runs in sloppy mode.
12. Should I use strict mode in new code?
In most cases you do not need to write the directive, because modules and classes are strict automatically. For classic scripts, adding it at the top of the file is a good practice. There is no reason to prefer sloppy mode in new code.
Key Takeaways
- Strict mode is opt-in and enabled by the exact string
"use strict";. - The directive must be the very first statement — a comment is fine, anything else is not.
- A misplaced directive fails silently; the code just runs in sloppy mode.
- ES modules and class bodies are strict automatically, with no opt-out.
- Strict mode turns undeclared assignment into a
ReferenceError. thisinside a plain function call isundefined, not the global object.- Duplicate parameter names, deleting a variable, and octal literals become syntax errors.
- Writing to a read-only or frozen property throws a
TypeError. evalandargumentsare restricted and cannot be assigned to.- Strict mode does not change scoping, the event loop, or performance meaningfully.
- It catches typos, lost
this, frozen-object writes, and duplicate parameters. - It does not catch logic errors — TypeScript is the tool for that.
What to Learn Next
Now that strict mode is clear, the next step is the execution model around it. Start with JavaScript Statements and Expressions and JavaScript Syntax Explained.
From there, explore JavaScript Functions Explained, JavaScript Hoisting Explained, and The this Keyword. For the language comparison, read JavaScript vs TypeScript.
If you are still connecting the pieces, read What Is JavaScript?, How to Add JavaScript to HTML, and How JavaScript Works for the surrounding context.
Practice Challenge
The fastest way to understand strict mode is to trigger each of the behaviours it changes and watch what happens. This exercise takes about twenty-five minutes.
- Create
index.htmlandapp.js, and load the script with<script src="app.js"></script>— a classic script, not a module. - In
app.js, assign to a variable you never declared and log the result. Confirm it works and that a global appeared onwindow. - Now add
"use strict";as the first line. Refresh and read theReferenceErrorin the console. - Move the directive to the second line, after another statement. Refresh and confirm the file is sloppy again.
- Add a comment above the directive and move it back to line one. Confirm strict mode works with a comment before it.
- Add a single semicolon on its own line before the directive. Refresh and confirm strict mode is silently disabled.
- Write a function that returns
thisand call it as a plain function. In sloppy mode confirm it is the window object; in strict mode confirm it isundefined. - Write a function with two parameters of the same name. Run it in sloppy mode and confirm the second wins. Then try to run it in strict mode and read the syntax error.
- Freeze an object with
Object.freeze()and try to write to a property. Confirm it fails silently in sloppy mode and throws aTypeErrorin strict mode. - Remove the directive and load the file with
<script type="module" src="app.js"></script>instead. Confirm that strict mode applies without the directive. - Rewrite the file as a
.jsmodule withexportand confirm it is still strict. - Finally, add a class with a private field to the file and confirm it works only when strict mode is active.
If you complete those steps, you have seen an undeclared assignment create a global and
then throw, watched the directive silently fail after a misplaced statement, triggered the
this change, produced a syntax error from duplicate parameters, watched a
frozen object reject a write in two different ways, and confirmed that modules and classes
are strict without any directive at all. Once you have caused each of those failures
yourself, strict mode stops being a line you copy from a template and becomes something you
understand.