February 6

Strict Mode in ECMAScript. Complete guide

There is a lot of information about the strict mode. However, there are very few articles covering the full range of features of the strict mode. Therefore, I have decided to compile my own guide of all restrictions, exceptions, and differences in the execution of "strict" code from "non-strict", in full compliance with the ECMA-262 specification.

What is a strict mode?

In the ECMA-262 specification, there are two concepts: non-strict mode and strict mode. Every syntactic unit of ECMAScript is always executed using the syntax and semantics of one or the other mode. Unlike the non-strict mode, the strict mode of execution imposes a series of syntactic restrictions and may even be executed differently.

Which code is executed in strict mode, and which in non-strict mode?

First of all, it is worth mentioning that once applied to a syntactic unit, it is not possible to cancel the strict mode during its execution. The specification does not provide any directives for this.

The strict mode applies to all Module code and all parts of Сlass code Class code. There is no way to disable the strict mode in such code.

All other code operates by default in non-strict mode, but it can be activated using the "use strict" directive in the Directive Prologue (the line preceding any other declarations).

Below are the possible ways to enable the strict mode in different types of code.

"use strict"
// Global code теперь работает в строгом режиме

eval(`"use strict"
// Eval code now works in strict mode
`);

function foo() {
  "use strict"
  // Function code now works in strict mode, the function itself now called "strict function"
}

new Function('a', 'b', `
"use strict";

// When using a Function constuctor, the strict mode can be enabled
// in the last arguments if the constructor which is a function body
return a + b;
`)

However, it is worth noting that strict mode cannot be enabled in every function. To be more specific, this mode is only available for functions with so-called simple (IsSimpleParameterList) parameters. In other words, it will not be possible to enable strict mode in the following cases.

function foo(a = 1) {
  // in case of using default values
  "use strict";
}

function foo(a, ...rest) {
  // in case of using rest paramters
  "use strict";
}

function foo({ a }) {
  // in case of using a destruction
  "use strict";
}

// Uncaught SyntaxError: Illegal 'use strict' directive in function with non-simple parameter list

Also, it is worth considering that the code inside strict mode will extend the mode to all nested syntactic units.

"use strict"

eval(`
// Eval code works in strict mode
`);

function foo() {
  // as well as Function code
  
  function bar() {
    // and nested functions either
  }
}

Reserved words

The reserved words are the words that cannot be used as identifiers. It is important not to confuse reserved words with keywords. Many keywords are reserved, but not all. Additionally, there are words that are reserved only in specific contexts (for example, "await" is reserved only inside an asynchronous function or module).
Specifically in strict mode, the following words are considered reserved:

implements, interface, let, package, private, protected, public, static, yield

This means that they cannot be used as identifiers.

"use strict"

let implements = "";

// Uncaught SyntaxError: Unexpected strict mode reserved word

Octal literal

In strict mode, its use is prohibited. Just in case, let me remind you, an octal literal begins with the character 0 (might be several) and then numbers 0, 1, 2, 3, 4, 5, 6, 7

"use strict"
01

// Uncaught SyntaxError: Octal literals are not allowed in strict mode.

Variables declaration and object properties

In non-strict mode, such a construction leads to the fact that a variable is declared in the global context, which is assigned a value

a = "a"

// Window {
//   ...
//   a: "a"
// }

In strict mode such a code is prohibited

"use strict"

a = "a";

// Uncaught ReferenceError: a is not defined

Also, in strict mode, the attribute { [[Writable]]: false } of the object's property is taken into account

"use strict"

const object = {};

Object.defineProperty(object, "a", {
    value: "a",
    writable: false,
});

object.a = "b";

// Uncaught TypeError: Cannot assign to read only property 'a' of object '#<Object>'

Accessor { [[Set]]:undefined } object's property will also cause an error

"use strict"

const object = {};

Object.defineProperty(object, "a", {
    set: undefined,
});

object.a = "b";

// Uncaught TypeError: Cannot set property a of #<Object> which has only a getter

Similarly, it's not allowed to add new properties to a non-extensible object.

"use strict"

const object = {};
Object.preventExtensions(object);

// Sealed objects are also considered non-extensible
// Object.seal(object);
//
// as well as freezed ones
// Object.freeze(object)

object.a = "a";

// Uncaught TypeError: Cannot add property a, object is not extensible

eval and arguments

If a variable has an identifier corresponding to either of these two keywords, it cannot perform assignment and unary update operations.

"use strict"

// Each line below will cause an error
// Uncaught SyntaxError: Unexpected eval or arguments in strict mode

let eval = 1;

let argumnets = 1;

eval++;

arguments--;

--eval;

++arguments;

Also, you cannot use these words as a function identifier.

"use strict"

function eval() {}
function arguments() {}

// Uncaught SyntaxError: Unexpected eval or arguments in strict mode

and use them as a parameter of the catch block

"use strict"

try {}
catch (arguments) {}

// Uncaught SyntaxError: Unexpected eval or arguments in strict mode

arguments

In a strict function (when strict mode is enabled inside the function itself), the arguments object cannot be overridden

function foo() {
    "use strict"
    arguments = [];
}

foo(1);

// Uncaught SyntaxError: Unexpected eval or arguments in strict mode

It can be noted that in early versions of JavaScript there was no other way to obtain a reference to the current function other than using arguments.callee. Now there is no such problem, and it is possible to freely use the function identifier inside the function itself, while using arguments.callee in strict functions is prohibited.

function foo() {
    "use strict"
    console.log(arguments.callee);
}

foo();

// Uncaught TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them

Instead, you should use the identifier of the function itself

function foo() {
    "use strict"
    console.log(foo);
}

foo();

// ƒ foo() {
//    "use strict"
//    console.log(foo);
// }

In addition, an important non-obvious point is that in a strict function, the arguments elements have no connection with direct arguments

function foo(a) {
    "use strict"
    arguments[0] = 2;
    
    console.log(a, arguments[0])
}

foo(1);

// 1 2

Here, by redefining the arguments element, the argument a itself remains unchanged.

In non-strict mode, such code will behave differently

function foo(a) {
    arguments[0] = 2;
    
    console.log(a, arguments[0])
}

foo(1);

// 2 2

Here, redefining the arguments element will also redefine the direct attribute.

In addition, you cannot in any way change or expand the arguments link and the non-standardized caller link of a strict function, even if the global environment itself is not in strict mode

function foo() {
    "use strict"
}

foo.caller = null
foo.arguments = null

foo();

// Uncaught TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them

eval

The eval code, in strict mode, cannot declare variables and functions in the VariableEnvironment of the calling environment. Instead, a new VariableEnvironment is created, available only inside the eval.

eval(`"use strict"
     var a = 1`);

console.log(a);

// Uncaught ReferenceError: a is not defined

In non-strict mode, the variable will be declared in the global environment

eval("var a = 1");

console.log(a);

// 1

Just in case, I'll clarify that we are specifically talking about the VariableEnvironment (a variable declared with var). In the case of let, const, and class, the variable is immediately declared in the LexicalEnvironment and, therefore, it will only be accessible within that environment, regardless of the mode.

this

In non-strict mode, if the context reference is empty (undefined or null), it automatically changes to a global context reference

function foo() {
    console.log(this);
}

foo();

// Window { ... }

In strict mode, the link will not change and will remain empty

"use strict"

function foo() {
    console.log(this);
}

foo();

// undefined

The same is true when using Function.prototype.apply and Function.prototype.call

"use strict"

function foo() {
    console.log(this);
}

foo.apply(null);

// null

delete

In strict mode, you cannot apply the delete operator to direct references to a variable, function argument, and function name

"use strict"

var a = 1;
delete a;

function foo(arg) {
    delete arg;
}

delete foo;

// Uncaught SyntaxError: Delete of an unqualified identifier in strict mode.

In general, this operator should be applied only to the properties of an object, for example

"use strict"

const object = {
    a: 1,
};

delete object.a;

// <- true

However, if an object's property has the attribute { [[Configurable]]: false } or is otherwise protected from deletion, it will also not be possible to delete it in strict mode

"use strict"

const object = {};

Object.defineProperty(object, "a", { configurable: false });

delete object.a;

// Uncaught TypeError: Cannot delete property 'a' of #<Object>

delete Object.prototype

// Uncaught TypeError: Cannot delete property 'prototype' of function Object() { [native code] }

with

In strict mode, you cannot use the with expression. Any attempts to apply it will cause a syntax error

"use strict"

const object = {};

with (object) {}

// Uncaught SyntaxError: Strict mode code may not include a with statement

Duplicate function parameters

In a strict function, an attempt to duplicate parameter names will result in a syntax error

function foo(a, a) {
    "use strict"
}

foo();

// Uncaught SyntaxError: Duplicate parameter name not allowed in this context

Similarly, when using the function constructor

const foo = new Function('a', 'a', `
"use strict";
`);

foo();

// Uncaught SyntaxError: Duplicate parameter name not allowed in this context

While in a non-strict function, duplication is possible

function foo(a, a) {
    console.log(a);
}

foo(1, 2);

// 2


My telegram channels:

EN - https://t.me/frontend_almanac
RU - https://t.me/frontend_almanac_ru

Русская версия: https://blog.frontend-almanac.ru/strict-mode