JavaScript Basics

JavaScript Strict Mode: Writing Safer and More Secure Code

Learn about JavaScript strict mode, how it helps catch common errors, improves performance, and enforces better coding practices. Complete guide with examples.

By JavaScript Document Team
strict-modesecuritybest-practiceses5fundamentals

Strict mode is a way to opt into a restricted variant of JavaScript that eliminates some JavaScript silent errors, improves performance, and helps you write more secure code. Introduced in ECMAScript 5, it's now a fundamental tool for modern JavaScript development.

What is Strict Mode?

Strict mode makes several changes to normal JavaScript semantics:

  • Eliminates some JavaScript silent errors by throwing errors
  • Fixes mistakes that make it difficult for JavaScript engines to perform optimizations
  • Prohibits syntax likely to be defined in future versions of ECMAScript

Enabling Strict Mode

// Enable strict mode for entire script
'use strict';

// All code below runs in strict mode
let x = 10;
console.log(x);

// Enable strict mode for a function
function strictFunction() {
  'use strict';
  // Function code is strict
  let y = 20;
  return y;
}

// This code is not in strict mode
var z = 30;

// Modern modules and classes are automatically strict
// ES6 modules
export function moduleFunction() {
  // Automatically strict, no need for "use strict"
}

// ES6 classes
class MyClass {
  // Class bodies are automatically strict
  constructor() {
    this.value = 10;
  }
}

Changes in Strict Mode

1. Preventing Accidental Globals

// Non-strict mode (bad)
function nonStrict() {
  accidentalGlobal = 10; // Creates global variable
  console.log(accidentalGlobal); // 10
}

// Strict mode (good)
function strict() {
  'use strict';
  // accidentalGlobal = 10; // ReferenceError: accidentalGlobal is not defined

  // Must declare variables
  let intentionalVariable = 10;
  console.log(intentionalVariable); // 10
}

// Common mistake: typos
function calculateTotal() {
  'use strict';
  let total = 100;

  // totla = total + 50; // ReferenceError (typo caught!)
  total = total + 50; // Correct

  return total;
}

2. Assignment Errors

'use strict';

// Cannot assign to non-writable properties
const obj = {};
Object.defineProperty(obj, 'fixed', {
  value: 42,
  writable: false,
});

// obj.fixed = 100; // TypeError: Cannot assign to read only property

// Cannot assign to getter-only properties
const person = {
  get name() {
    return 'John';
  },
};

// person.name = "Jane"; // TypeError: Cannot set property name

// Cannot assign to non-extensible objects
const frozen = Object.freeze({ x: 1 });
// frozen.y = 2; // TypeError: Cannot add property y

// Cannot delete non-configurable properties
const config = {};
Object.defineProperty(config, 'constant', {
  value: 'unchangeable',
  configurable: false,
});

// delete config.constant; // TypeError: Cannot delete property

3. Duplicate Parameters

// Non-strict mode (allowed but confusing)
function nonStrictDuplicates(a, b, a) {
  console.log(a); // Last 'a' value
  return a + b;
}

console.log(nonStrictDuplicates(1, 2, 3)); // 5 (3 + 2)

// Strict mode
function strictNoDuplicates(a, b, c) {
  'use strict';
  // function strictDuplicates(a, b, a) {} // SyntaxError: Duplicate parameter name
  return a + b + c;
}

4. Octal Literals

// Non-strict mode
console.log(010); // 8 (octal)
console.log(0o10); // 8 (ES6 octal syntax, allowed)

// Strict mode
('use strict');
// console.log(010); // SyntaxError: Octal literals are not allowed

// Use explicit octal notation
console.log(0o10); // 8 (allowed in strict mode)
console.log(parseInt('10', 8)); // 8 (explicit conversion)

5. this Binding Changes

// Non-strict mode
function nonStrictThis() {
  console.log(this); // window (in browser) or global (in Node.js)
}

nonStrictThis(); // Called as function

// Strict mode
function strictThis() {
  'use strict';
  console.log(this); // undefined
}

strictThis(); // undefined

// Object method calls still work normally
const obj = {
  method() {
    'use strict';
    console.log(this); // obj
  },
};

obj.method();

// Explicit binding still works
strictThis.call(obj); // obj
strictThis.apply(obj); // obj
const boundFunc = strictThis.bind(obj);
boundFunc(); // obj

6. Arguments Object Changes

'use strict';

// Arguments doesn't track parameter changes
function strictArguments(a, b) {
  a = 10;
  b = 20;

  console.log(a, b); // 10, 20
  console.log(arguments[0], arguments[1]); // Original values passed
}

strictArguments(1, 2);

// Cannot assign to arguments
function noArgumentsAssignment() {
  'use strict';
  // arguments = []; // SyntaxError: Unexpected eval or arguments
}

// arguments.callee is forbidden
function noCallee() {
  'use strict';
  // console.log(arguments.callee); // TypeError
}

7. eval Changes

// Strict mode eval has its own scope
'use strict';

let x = 1;
eval('var x = 2; console.log(x);'); // 2
console.log(x); // 1 (unchanged)

// eval and arguments cannot be used as variable names
// let eval = 10; // SyntaxError
// let arguments = 20; // SyntaxError

// Function declarations inside eval are scoped
eval('function f() { return 1; }');
// console.log(typeof f); // undefined (not available outside eval)

8. Delete Restrictions

'use strict';

// Cannot delete variables
let x = 1;
// delete x; // SyntaxError

// Cannot delete functions
function myFunc() {}
// delete myFunc; // SyntaxError

// Can still delete object properties
const obj = { prop: 1 };
delete obj.prop; // OK
console.log(obj.prop); // undefined

// Cannot delete non-configurable properties
const fixed = {};
Object.defineProperty(fixed, 'constant', {
  value: 42,
  configurable: false,
});
// delete fixed.constant; // TypeError

Reserved Words

Strict mode reserves certain words for future JavaScript versions:

'use strict';

// These are reserved keywords
// let implements = 1; // SyntaxError
// let interface = 2; // SyntaxError
// let package = 3; // SyntaxError
// let private = 4; // SyntaxError
// let protected = 5; // SyntaxError
// let public = 6; // SyntaxError
// let static = 7; // SyntaxError
// let yield = 8; // SyntaxError

// Use different names
let implementation = 1;
let apiInterface = 2;
let packageData = 3;

Strict Mode in Different Contexts

Global Strict Mode

// entire-script.js
'use strict';

// Everything in this file is strict
var globalStrict = true;

function allStrict() {
  // This is also strict
  return true;
}

// Concatenating scripts can be problematic
// script1.js: "use strict";
// script2.js: (non-strict)
// Result: Only script1 code is strict

Function-Level Strict Mode

// Mixed mode file
var nonStrictGlobal = 1;

function nonStrictFunc() {
  undeclared = 10; // Creates global (bad)
}

function strictFunc() {
  'use strict';
  // undeclared = 10; // ReferenceError
  let declared = 10; // Good
}

// Recommended: Use strict mode for entire files

Module Strict Mode

// ES6 modules are always strict
// module.js
export function alwaysStrict() {
  // No need for "use strict"
  // undeclared = 10; // ReferenceError
}

// HTML inline modules
// <script type="module">
//   // Automatically strict
//   let x = 10;
// </script>

Performance Benefits

Strict mode can improve performance by allowing JavaScript engines to make optimizations:

'use strict';

// Easier optimization examples

// 1. No with statements (forbidden in strict)
// with statements prevent optimization

// 2. eval doesn't introduce new variables
function optimizable(data) {
  eval('var computed = data * 2');
  // 'computed' is local to eval in strict mode
  return data; // Engine can optimize better
}

// 3. arguments object is more predictable
function fastFunction(a, b) {
  // Arguments doesn't alias parameters
  a = 10;
  return arguments[0] + b; // Original value of a
}

// 4. No arguments.callee reduces overhead
function recursive(n) {
  // Use named function instead of arguments.callee
  return n <= 1 ? 1 : n * recursive(n - 1);
}

Common Strict Mode Patterns

Safe Constructors

'use strict';

// Constructor function
function Person(name) {
  // Strict mode prevents forgetting 'new'
  this.name = name;
}

// const person = Person("John"); // TypeError: Cannot set property 'name' of undefined
const person = new Person('John'); // Correct

// ES6 classes are always strict and enforce new
class ModernPerson {
  constructor(name) {
    this.name = name;
  }
}

// const p = ModernPerson("Jane"); // TypeError: Class constructor cannot be invoked without 'new'

Safer Feature Detection

'use strict';

// Feature detection without creating globals
function hasFeature(feature) {
  try {
    // Test feature without side effects
    return typeof window[feature] !== 'undefined';
  } catch (e) {
    return false;
  }
}

// Instead of accidentally creating globals
function badDetection() {
  // someFeature = someFeature || fallback; // Creates global!
}

Module Pattern

// IIFE with strict mode
const myModule = (function () {
  'use strict';

  // Private variables
  let privateVar = 0;

  // Private functions
  function privateFunc() {
    return privateVar++;
  }

  // Public API
  return {
    increment() {
      return privateFunc();
    },

    getValue() {
      return privateVar;
    },
  };
})();

console.log(myModule.increment()); // 0
console.log(myModule.getValue()); // 1
// console.log(privateVar); // ReferenceError

Migration to Strict Mode

Testing Existing Code

// Wrap existing code to test
function testStrictMode() {
  'use strict';

  // Paste existing code here
  // Fix any errors that appear
}

// Gradual migration
// 1. Add "use strict" to new files
// 2. Add to individual functions in old files
// 3. Fix issues as they appear
// 4. Eventually make entire files strict

// Common issues to fix:
// - Undeclared variables
// - Duplicate parameters
// - Octal literals
// - with statements

Strict Mode Linting

// ESLint configuration
{
  "rules": {
    "strict": ["error", "global"], // Enforce strict mode
    "no-implicit-globals": "error", // Prevent accidental globals
    "no-undef": "error", // Prevent undeclared variables
  }
}

// JSHint
/* jshint strict: true */

// TypeScript automatically enforces many strict mode benefits

Best Practices

1. Always Use Strict Mode

// Modern JavaScript files should start with
'use strict';

// Or use ES6 modules which are automatically strict
export default function () {
  // Automatically strict
}

2. Use Build Tools

// Webpack, Rollup, etc. can add strict mode
// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        use: {
          loader: 'babel-loader',
          options: {
            plugins: ['transform-strict-mode'],
          },
        },
      },
    ],
  },
};

3. Combine with Linters

// .eslintrc.json
{
  "parserOptions": {
    "ecmaVersion": 2021,
    "sourceType": "module" // Implies strict mode
  },
  "env": {
    "es6": true
  },
  "rules": {
    "strict": ["error", "never"] // Not needed in modules
  }
}

4. Be Consistent

// Bad: Mixed strict and non-strict
function inconsistent() {
  // Non-strict function

  function inner() {
    'use strict';
    // Strict inner function (confusing)
  }
}

// Good: Consistent throughout file
('use strict');

function consistent() {
  // All functions in file are strict
}

Common Pitfalls

Concatenation Issues

// file1.js
'use strict';
function strict1() {}

// file2.js (non-strict)
function nonStrict2() {}

// concatenated.js
// Only strict1 is in strict mode!

// Solution: Use module system or wrap in IIFE
(function () {
  'use strict';
  // file1 content
})();

(function () {
  // file2 content (non-strict)
})();

Library Compatibility

// Some old libraries may not work in strict mode
'use strict';

// Old library code that uses arguments.callee, with, etc.
// May throw errors

// Solution: Load libraries separately
// <script src="old-library.js"></script> <!-- Non-strict -->
// <script src="my-strict-app.js"></script> <!-- Strict -->

Conclusion

Strict mode is an essential tool for writing robust JavaScript code. It catches common programming errors, prevents potentially problematic syntax, and can improve performance. While it may seem restrictive at first, the benefits far outweigh the constraints. Modern JavaScript development should always use strict mode, either explicitly with "use strict" or implicitly through ES6 modules and classes. By adopting strict mode, you're writing safer, more maintainable, and more future-proof JavaScript code.