JavaScript Strict Mode: “use strict” Explained

JavaScript Strict Mode: “use strict” Explained

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.

In one sentence: Strict mode is an opt-in mode enabled by the directive "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.

Sloppy mode

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 mode

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.

A little history

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.

File level

Top of the file

Every line of the script runs in strict mode, including functions defined inside it.

Function level

Top of a function body

Only that function is strict. Everything outside it stays in sloppy mode.

Automatic

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>
If you use modules, you already use strict mode

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.

“use strict”; const a = 1;
The directive is the first statement, so the file is strict.
Strict
// A comment “use strict”; const a = 1;
A comment before it does not count as a statement, so this still works.
Strict
const a = 1; “use strict”;
The directive is no longer first. It becomes a plain string expression and does nothing.
Sloppy
; “use strict”;
An empty statement counts as a statement. This silently disables strict mode.
Sloppy
‘use strict’; “use strict”;
Single quotes work too. The directive is just a string; either quote style is valid.
Strict
A silent failure

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

ReferenceError

Undeclared assignment

Sloppy: silently creates a global variable.

Strict: throws ReferenceError.

SyntaxError

Deleting a variable

Sloppy: does nothing, no error.

Strict: syntax error — the script will not parse.

SyntaxError

Duplicate parameter names

Sloppy: the last parameter wins silently.

Strict: syntax error.

SyntaxError

Octal literals

Sloppy: 010 means 8.

Strict: syntax error. Use 0o10 instead.

SyntaxError

The with statement

Sloppy: allowed, and rarely used.

Strict: forbidden entirely.

TypeError

Writing to read-only

Sloppy: fails silently.

Strict: throws TypeError.

TypeError

Adding properties to frozen objects

Sloppy: fails silently.

Strict: throws TypeError.

SyntaxError

Reserved words

Strict reserves implements, interface, let, package, private, protected, public, static, yield.

Group 2 — Changes to this

Sloppy mode — this is the global object
function whoAmI() { return this; } whoAmI() === window // true // A plain function call sets // this to the global object. // This is how accidental globals // get created.
Strict mode — this is undefined
"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`.
Why this matters less than it used to

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.

Every behaviour strict mode changes
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
What strict mode does not change

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.
The private field connection

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
The pattern is always the same

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

Silently disabled
// 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.
No error at all The most common strict-mode mistake. The file looks correct and is not strict.

2. An empty statement before the directive

; “use strict”;
The lone semicolon is an empty statement. It counts, and it disables the directive.
Sloppy
; “use strict”;
Yes, really. This exact pattern has cost people entire afternoons.
Sloppy

3. Expecting strict mode to cover a whole file when only a function is marked

Incorrect — only one function is strict
function first() { "use strict"; // strict here } function second() { // sloppy here undeclared = 5; // creates a global } second(); // no error
Correct — the directive at the top of the file
"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.
Strict mode will not tell you it is off

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.

1️⃣

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.

The one-line summary

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.
  • this inside a plain function call is undefined, 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.
  • eval and arguments are 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.

  1. Create index.html and app.js, and load the script with <script src="app.js"></script> — a classic script, not a module.
  2. In app.js, assign to a variable you never declared and log the result. Confirm it works and that a global appeared on window.
  3. Now add "use strict"; as the first line. Refresh and read the ReferenceError in the console.
  4. Move the directive to the second line, after another statement. Refresh and confirm the file is sloppy again.
  5. Add a comment above the directive and move it back to line one. Confirm strict mode works with a comment before it.
  6. Add a single semicolon on its own line before the directive. Refresh and confirm strict mode is silently disabled.
  7. Write a function that returns this and call it as a plain function. In sloppy mode confirm it is the window object; in strict mode confirm it is undefined.
  8. 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.
  9. Freeze an object with Object.freeze() and try to write to a property. Confirm it fails silently in sloppy mode and throws a TypeError in strict mode.
  10. Remove the directive and load the file with <script type="module" src="app.js"></script> instead. Confirm that strict mode applies without the directive.
  11. Rewrite the file as a .js module with export and confirm it is still strict.
  12. 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.

Inar Learn
Inar Learnhttps://inarlearn.com
Inar Learn is an innovative online learning platform offering high-quality courses, tutorials, and resources to help learners gain practical skills and grow their knowledge.

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here