in ECMAScript

ECMA-262-5 in detail. Chapter 2. Strict Mode.

Introduction

In this chapter we will concentrate on another feature of the ECMA-262-5. The topic of the discussion is a strict variant (or strict mode) of an ECMAScript program. We’ll discuss the reasons for providing this feature, how it affects on some already existing semantic aspects and what can it restrict.

Strict mode features

Below the general provisions regarding strict variant of the ECMAScript language are described. Let’s start from the reasons for which strictness was made and definition of this mode.

Reasons

The current version of the specification — ES5 doesn’t change the syntax much. As one of the incompatible syntactic innovations can be mentioned e.g. an accessor property (getter/setter) defined in the declarative view, with using object initialiser. Although, even this new syntactic feature isn’t new for some implementations (e.g. for SpiderMonkey), which had such constructions before standardizing the ES5. Some other new syntax constructions and ideological aspects which can be based on that new syntax are leaved for the future ECMAScript version — ES6 aka Harmony. Instead, all innovations of the ES5 are made in the context of already existed ES3 syntax constructs. From this viewpoint, ES5 and namely all new meta-methods of the Object constructor (such as e.g. Object.create and other) can be treated as just an additional library (a framework) which is “loaded” before any other library used in a project.

However, without changing the syntax, ES5 provides some important semantic innovations, as e.g. ability to control property attributes, objects extensibility, already mentioned accessor properties, etc. All these features are the good addition for the ES3.

At the same time some features of ES3 were recognized as error-prone, not enough secure or having poor errors checking at parsing stage. In addition, some ES3 features were marked as deprecated and possibly will be removed from the next version of the language. From this point of view, ES5 is a transitional version of the specification.

For that purpose, ECMA-262-5 defines a strict variant of the language. This variant excludes some specific syntactic and semantic features of the regular ECMAScript language and modifies the detailed semantics of some features. The strict variant also specifies additional error conditions that must be reported by throwing error exceptions in some situations that are not specified as errors by the non-strict form.

The strict variant of ECMAScript is commonly referred to as the strict mode of the language.

Definition

Strict mode syntax and semantics of ECMAScript is explicitly made at the level of individual ECMAScript code units. That means we can choose whether to process some code unit (e.g. a function) with the strict mode or not.

To define a strict mode we should use a Use Strict Directive of so-called a Directive Prologues.

A Directive Prologues is a sequence of directives represented entirely as string literals which followed by an optional semicolon. A Directive Prologue may be an empty sequence.

More generally (regardless ECMAScript), a Directive Prologues corresponds to a prologue code i.e. some automatically generated code, preprocessing options, or directives.

For example in ECMAScript:

// start of a directive prologue
"first directive";
'the second in the single quotes';
// end of a directive prologue

The meaning of some such directives is special. Currently, ES5 defines only one special directive — already mentioned a Use Strict Directive.

A Use Strict Directive is a directive in a Directive Prologue whose string literal is either the exact character sequences "use strict" or 'use strict'. A Use Strict Directive may not contain an escape sequence or line continuation. This directive is used for specifying a strict mode of a code unit.

So, to define a strict mode of some code unit we use simply:

"use strict";
// implementation of a module

Notice, that a Directive Prologue may contain more than one Use Strict Directive. However, an implementation may issue a warning if this occurs.

"use strict";
"use strict"; // may be a warning

In addition to the specification, an implementation can use any other directive with specific meaning for the particular implementation. If some directive is not recognized as a special, a warning can be issued. For example, if the implementation reserves additionally “check arguments” directives, then a warning is issued only for the third directive in the following example:

"use strict";
"check arguments";
"use paranoid"; // may be a warning

Strictness scope

As we mentioned, we can choose a strictness for a particular code unit, defining a scope of the strict variant. In the following example, we do not use the strict mode for the whole program, but use it only for the foo function:

var eval = 10; // OK

function foo() {
  "use strict";
  alert(eval); // 10
  eval = 20; // SyntaxError
}

foo();

We see the SyntaxError in the second case — that’s because eval identifier is not allowed for assignment in strict mode, although the first assignment, being evaluated in non-strict mode, occurred normally. But all such restrictions we will discuss shortly in detail.

Another thing to note, is that a Directive Prologue is required to be placed exactly at the top of a context code:

// OK, non-strict mode
(function foo() {
  arguments = 10;
  "use strict";
})();

// SyntaxError, strict-mode
(function foo() {
  "use strict";
  arguments = 10;
})();

It though doesn’t require the "use strict"; directive to be placed at the first position (again — it’s only the whole Directive Prologue should be placed as initial statement):

// also strict mode

"use restrict";
"use strict";

a = 10; // ReferenceError

Besides, the affecting of a strict mode prolongs to all inner contexts. I.e. the context code is a strict code if either its context or any surrounding context contains a Use Strict Directive:

// define strict mode in the global context,
// i.e. for the whole program
"use strict";

(function foo() {
  // strictness is "inherited" from
  // the global context
  eval = 10; // SyntaxError
  (function bar() {
    // the same - from the global context
    arguments = 10; // SyntaxError
  })();
})();

At the same time a strict mode is specified lexically (statically) for a context — just as a closure — where this context is created, but not executed. I.e. the strictness of the context being called is not dependent on the strictness of a caller:

// globally use a non-strict mode

var foo = (function () {
  "use strict";
  return function () {
    alert(this);
  };
})();

function bar() {
  alert(this);
}

// for both functions, a caller is the global context

// but "foo" is evaluated in the strict mode
foo(); // undefined

// meanwhile "bar" is not
bar(); // object

Another important point to notice, that functions created via the Function constructor do not inherit the strictness from the surrounding context. The following example does not throw an exception:

"use strict";

var f = Function("eval", "arguments", " \
  eval = 10; arguments = 20; \
  with ({a: 30}) { \
    alert(a + eval + arguments); \
  }"
);

f(); // OK, 60

But if a strict mode directive is inside such a function, then all restrictions are available:

// non-strict globally

var f = Function("eval", "'use strict'; alert(eval);"); // SyntaxError

And one more important feature related with the strictness scope is an indirect eval call, which in detail will be discussed below. As we’ll see shortly, direct eval cannot create a variable in the environment of the calling context. However, indirect eval call, may create a variable in the global scope, even if the global code is strict. In other words, global strictness scope does not prolong and does not affect indirect evals:

"use strict";

eval("var x = 10"); // direct "eval"

("indirect", eval)("var y = 20"); // indirect "eval"

console.log(typeof x, typeof y); // "undefined", "number"

Repeat, the indirect eval itself is discussed in detail below, so don’t worry if for now the code above with this ("indirect", eval)(...) looks a bit unfamiliar; we’ll clarify it.

Now we are ready to discuss all the cases applied to the strict mode.

Strict mode restrictions

Here you can find all restrictions provided by the strict mode of the ECMA-262-5. Some of them, having similar semantics (e.g. eval and arguments identifiers are not allowed for assignment or function argument names) are combined in one subsection.

At the moment of writing this article (2010-06-01), the only available implementation for testing the strict mode (and the complete implementation of the ES5) — is the BESEN engine written by Benjamin Rosseaux. It of course still has some bugs (some of them I’ve reported while was writing this article and glad to see that several bugs are already fixed), but it is a good candidate for ES5 programming and testing. Implementation is open source, written in Object Pascal. You can download the sources and the IDE from which you can run your tests. Hope, some major implementation will follow soon also.

So, let’s see which restrictions we have in the strict mode of the ES5.

Future reserved keywords

The following set of identifiers is classified as future reserved keywords and cannot be used as variable or function names: implements, interface, let, package, private, protected, public, static, and yield.

// non-strict mode

var lеt = 10; // OK
console.log(lеt); // 10
"use strict";

var let = 10; // SyntaxError

Notice though, that some of the identifiers are already were used as special keywords in several implementations. For example, yield and let are the keywords of the SpiderMonkey since 1.7 version:

// SpiderMonkey 1.7

let x = 10;
console.log(x); // 10

Octal extension for literals

A conforming implementation, when processing strict mode code, may not extend the syntax of numeric literals and escape sequence with octal semantics.

In ES3, if an implementation extended the numeric literal syntax, we had the following case:

// ES3 with octal extension

var x = 010; // octal number
print(x); // 8 - in octal radix

Notice, that even in ES3 octal extension for numeric and string literals was only for backward compatibilities and was defined only in the Annex B (see B.1 Additional Syntax) but not in the main section of numeric or string literals. The same in the ES5 specification — octal extension may be used only for backward compatibilities and only in non-strict mode. It’s also defined in the Annex B of ES5. In strict mode, as we said, octal extension is forbidden:

"use strict";

var x = 010; // SyntaxError

The same with escape sequence, e.g. “\0“.

This restriction mostly relates to well-known confusion with often used parseInt function, when programmers get “unexpected” result for e.g. “08” value:

// ES3

parseInt("07"); // 7
parseInt("08"); // 0 ?
parseInt("09"); // 0 ?
parseInt("010"); // 8 ?

That exactly related with the octal extension — 08 isn’t a valid octal number, so it is treated as 0 in parseInt (notice, however, that parseInt had in ES3 its own, independent from numeric literals, handling of octal numbers). To solve the issue a needed radix should be specified in ES3:

parseInt("08", 10); // 8

In ES5, handling of octal case in parseInt algorithm has been removed, so now we can use it without specifying a radix in this case. Notice again, that new parseInt algorithm is applied regardless strict mode and regardless numeric grammar extension:

// ES5, no matter, strict
// or non-strict mode

parseInt("08"); // 8

But, I want to remind, that parseInt is for parsing (that’s why it’s named so) a number from a string. That means, this method does something more than just a type conversion:

parseInt("08Gb", 10); // 8

If you need just a type conversion, an alternative way can be used, e.g. applying the Number constructor as a function — because it exactly performs ToNumber conversion in this case. If the value of an argument is known and can be converted to number, it can be (and could in ES3) safely used without specifying any radix:

Number("08"); // 8

// alternative way
+"08"; // 8

Of course, it won’t parse an argument as parseInt does:

Number("08Gb"); // NaN

Assignment to an undeclared identifier

As we know, in ES3 assignment to an undeclared identifier creates a new property of the global object (which are often confused with global variables). This causes also well-known issue related with unintentional polluting of the global scope:

// non-strict mode

(function foo() {
  // local vars
  var x = y = 20;
})();

// unfortunately, "y" wasn't local
// for "foo" function
alert(y); // 20
alert(x); // "x" is not defined

In strict mode this feature has been removed:

"use strict";
a = 10; // ReferenceError

var x = y = 20; // also a ReferenceError

There is a pair of subtle cases related with this restriction. According to 11.13.1 Simple Assignment algorithm, first — the left part of the expression is evaluated. This means, that even the grouping operator cannot help to manage such an assignment to an undeclared identifier, which actually can be already declared after the work of the grouping operator:

"use strict";

// The left part is evaluated first and
// returns an unresolved reference, i.e.
// at the moment - "undeclared" does not exist.
//
// However, in the grouping operator, which
// logically should form a priority of an evaluation,
// we create "undeclared" property (and even see
// that alert correctly shows 10).
//
// But even it's created, the assignment cannot
// work because we already evaluated an unresolved
// reference according to the 11.13.1 algorithm

// ReferenceError
undeclared = (this.undeclared = 10, alert(undeclared), 10);

On the other hand, the similar case with delete operator works logically in the correct precedence order, i.e. first — a property is deleted inside the grouping operator, and then we already try to assign a value to the already undeclared identifier:

"use strict";

// if use "prefix" (base), we may
// easily assign to a new property, because
// now the reference is resolved

this.declared = 10; // OK

// since it's delacred, we may even
// change it without base

declared = 20; // OK

// However, with the next evaluation
// we also have an error, but now
// according to 8.7.2.

declared = (delete this.declared, 30); // ReferenceError

In the example above, again, first — the left part is evaluated (and in this case it’s already resolved, because “declared” property exists). Then we delete the property and at the last step — try to assign a value 30 to the already unresolved reference. This case is caught by the step 3-a of the 8.7.2 PutValue algorithm.

However, regarding the delete operator, there is one more subtle case related with removing properties of the environment records. This case we’ll consider shortly below in the section devoted to delete operator restrictions.

Assignment to read-only properties

The same situation is with read-only properties, i.e. if a property being either a data property with [[Writable]] attribute set to false or an accessor property without a [[Set]] method. In the following example a TypeError should be thrown:

"use strict";

var foo = Object.defineProperties({}, {
  bar: {
    value: 10,
    writable: false // by default
  },
  baz: {
    get: function () {
      return "baz is read-only";
    }
  }
});

foo.bar = 20; // TypeError
foo.baz = 30; // TypeError

However, if a property is configurable, then we can change it via Object.defineProperty:

"use strict";

var foo = Object.defineProperty({}, "bar", {
  value: 10,
  writable: false, // read-only
  configurable: true // but still configurable
});

// change the value
Object.defineProperty(foo, "bar", {
  value: 20
});

console.log(foo.bar); // OK, 20

// but still can't via assignment

foo.bar = 30; // TypeError

Shadowing inherited read-only properties

Notice, that it relates to inherited properties as well. If we try to shadow some read-only inherited property via assignment we also get TypeError. And again, if we shadow the property via Object.defineProperty it’s made normally — thus, a configurable attribute of the inherited property doesn’t matter in this case:

"use strict";
 
var foo = Object.defineProperty({}, "x", {
  value: 10,
  writable: false
});
 
// "bar" inherits from "foo"
 
var bar = Object.create(foo);

console.log(bar.x); // 10, inherited
 
// try to shadow "x" via assignment
 
bar.x = 20; // TypeError

console.log(bar.x); // still 10, if non-strict mode
 
// however shadowing works
// if we use "Object.defineProperty"
 
Object.defineProperty(bar, "x", { // OK
  value: 20
});

console.log(bar.x); // 20

In ES3 and non-strict ES5 such assignments to read-only properties failure silently.

Creating a new property of non-extensible objects

Restricted assignment also relates to augmenting non-extensible objects, i.e. objects having [[Extensible]] property as false:

"use strict";

var foo = {};
foo.bar = 20; // OK

Object.preventExtensions(foo);
foo.baz = 30; // TypeError

eval and arguments restrictions

In strict mode these names — eval and arguments are treated as kind of “keywords” (while they are not) and not allowed in several cases.

They cannot appear as a variable declaration or as a function name:

"use strict";

// SyntaxError in both cases
var arguments;
var eval;

// also SyntaxError
function eval() {}
var foo = function arguments() {};

They are not allowed as function argument names:

"use strict";

// SyntaxError
function foo(eval, arguments) {}

It is impossible to assign new values to them (thus, arguments is restricted in the global scope also, but not only in functions):

"use strict";

// SyntaxError
eval = 10;
arguments = 20;

(function (x) {
  alert(arguments[0]); // 30
  arguments = 40; // TypeError
})(30);

They cannot be used with prefix and postfix increment/decrement expressions:

"use strict";

// SyntaxError
++eval;
arguments--;

Regarding objects and their properties, eval and arguments are allowed in assignment expressions and may be used as property names of objects:

"use strict";

// OK
var foo = {
  eval: 10,
  arguments: 20
};

// OK
foo.eval = 10;
foo.arguments = 20;

However, eval and arguments are not allowed as parameter names of declarative view of a setter:

"use strict";

// SyntaxError
var foo = {
  set bar (eval, arguments) {
    ...
  }
};

An implementation may not associate special meanings within strict mode functions to properties named caller or arguments of function instances. It is impossible to create or modify properties with these names on function objects in strict mode (caller and callee restrictions will be described in the following section):

"use strict";

function foo() {
  alert(foo.arguments); // SyntaxError
  alert(foo.caller); // SyntaxError
}

foo();

Also, neither eval, nor arguments cannot be used as an identifier of a catch clause:

"use strict";

try {
  throw Error("...");
} catch (arguments) {} // SyntaxError, the same for "eval" name

Besides restrictions for these names, arguments has additional restriction in semantics. In the strict mode it does not dynamically share its array indexed property values with the corresponding formal parameter bindings of a function. So, array indexed property values of the arguments object are just static copies:

"use strict";

(function foo(x) {

  alert(arguments[0]); // 10
  arguments[0] = 20;

  alert(x); // 10, but not 20

  x = 30;
  alert(arguments[0]); // 20, but not 30

})(10);

Implementation of eval also has restriction in semantics in this case — it cannot instantiate a new binding (a variable or function) in the calling context (as it does in non-strict mode). Instead, strict code for eval is executed in its own “sandbox environment”, which is being destroyed after the eval ends:

"use strict";

eval("var x = 10; alert(x);"); // 10
alert(x); // "x" is not defined

Notice, there is an important exception from this rule — an indirect eval (will be discussed below), which allows to create a global variable regardless even that the global code is strict:

"use strict";

("indirect", eval)("var x = 10; alert(x);"); // 10
alert(x); // 10

callee and caller restrictions

Just like eval and arguments names, callee and caller identifiers because of some security reasons have been restricted. For example:

// non-strict mode

function foo(x, y) {
  alert(x); // 10
  bar();
  alert(x); // 100
}

function bar() {
  console.dir(bar.caller.arguments); // 10, 20
  bar.caller.arguments[0] = 100;
}

foo(10, 20);

In non-strict mode, arguments.callee still can be used to reference an anonymous function from inside, e.g. for a recursive call. Unfortunately, in strict mode if we want to call a function expression recursively (or just to reference it) we should define a NFE (name function expression):

"use strict";

(function foo(bar) {
  if (!bar) {
    arguments.callee(true); // SyntaxError
    foo(true); // OK
  }
})();

There is one unpleasant consequence related with removing arguments.callee from the strict mode — now it’s not possible to reference a function created via the Function constructor from the inside (e.g. at recursive call) if such function created not in the global context. As we know, the scope chain of Function functions consists only of the global object. Therefore, if such function created inside other function, no any binding name of surrounding function is available for the Function function. We could call such functions in ES3 only via the arguments.callee reference. In ES5 — there is no (at least normal) way to call such function recursively:

(function () {
  
  // outer name is not available,
  // regardless strictness
  var foo = Function("alert(foo);'");
  foo(); // "foo" is not defined (no such name in the global context)
  
  // error in strict mode for arguments.callee
  Function("'use strict; alert(arguments.callee);'")(); // TypeError
  
  // OK in non-strict for arguments.callee
  Function("alert(arguments.callee);'")(); // OK, function

})();

However, if such a strict function is created in the global context, we can use outer variable name to reference it:

var foo = Function("'use strict; alert(foo);'");
foo(); // function

As a variant of solving the issue, a function can be passed as an argument to itself:

(function () {
  var foo = Function("foo", "'use strict'; alert(foo);");
  foo(foo); // OK, function
})();

There can be other tricks for particular cases of the recursive call, e.g. with using constructor property and new operator:

(function () {
  var foo = Function('bool', 'return bool || this.constructor(true);');
  new foo(); // will be called twice
})();

But obviously, such tricks are inconsistent and not for practical usage. The other way of dynamic code creation with using Function constructor can be e.g. this approach, thought we create two functions:

(function () {
  var foo = Function('return function bar() {alert(bar);}')();
})();

Regarding the caller property — actually, it was not standardized at all in ES3 — neither as a direct property of a function, nor as a property of the arguments object. However, it is implemented in many current engines. It was marked as obsolete by SpiderMonkey long time ago. So, you may use it at your own risk, although it, being deprecated, is not recommenced.

As a convenient application of the arguments.caller can be mentioned e.g. an implementation of a super concept in a system with the hierarchical inheritance. I.e. a convenient way of calling a parent method (in the prototype chain) with the same name from a child method. For example:

var A = Class({
  foo: function () {
    return "A#foo";
  }
});

var B = Class({
  extends: A,
  foo: function () {
    return this.super() + ", B#foo";
  }
});

// an instance of a "class"
var b = new B();
b.foo(); // "A#foo, B#foo"

//  implementation of Class
// ...
// this.super via "caller" property
super: function () {
  var caller = arguments.caller;
  return caller.class.superproto[caller.name].apply(this, arguments);
}

Thus, an implementation of a Class “meta-constructor” should mark every method with additional class (a direct reference to a “class” instead of using this.constructor — to avoid a recursion on third and deeper inheritance levels when applying this) and name properties (to be able to call appropriate parent method in the super function; if a function name is specified, functions of some implementations, e.g. of the SpiderMonkey already have that name property). As an example, you can examine e.g. this implementation. But that’s already another topic. The main goal is to show, that a correct caller value can be useful for a convenient code reuse implementation without specifying a long parent constructor’s name in the classical approach of that pattern which we discussed in the chapter 7.2 of ES3 series devoted to OOP in ECMAScript:

// much convenient to write
foo: function () {
  return this.super() + " own";
}

// instead of
foo: function () {
  ChildConstructor.superproto.foo.apply(this, arguments) + " own";
}

There is another approach to use the same super method — wrapping each child method with the same name with a special wrapper-function which will set correct this.super value, get the result, restore back this.super and return the result. But, repeat, this is already another story, and now we’re just saying that because of a caller was recognized as error-pron and insecure, it was removed from the strict mode:

"use strict";

(function foo() {
  alert(arguments.caller); // SyntaxError
  alert(foo.caller); // SyntaxError
})();

Notice also another mentioned innovation of ES5 — it is possible to use keywords as property names. That exactly why we were able to use this.super in the examples above, which provides a SyntaxError in ES3, because super is a future reserved keyword. In ES5 we can normally use them:

var foo = {
  function: function () {
    alert(10);
  },
  super: function () {
    this.function();
  }
};

foo.super(); // 10

Duplications

Duplications of data property names in an object initialiser are restricted:

"use strict";

// SyntaxError
var foo = {
  x: 10,
  x: 10
};

Duplications of formal parameters of functions are restricted as well:

"use strict";

// SyntaxError
function foo(x, x) {}

Notice, that duplications of accessor properties (in case of duplicating the same operation, i.e. get or set) are restricted regardless the strict mode:

// strict or non-strict mode

// SyntaxError
var foo = {
  get x() {},
  get x() {}
};

// the same with setters
// SyntaxError
var bar = {
  set y(value) {},
  set y(value) {}
};

Also duplications of mixed property types (i.e. both — a data and an accessor property with the same name) are restricted regardless the strict mode:

// strict or non-strict mode

// SyntaxError
var foo = {
  x: 10,
  get x() {}
};

delete operator restrictions

Applied in a strict code, delete operator cannot remove non-configurable properties (i.e. properties having [[Configurable]] attribute as false) and provides SyntaxError or TypeError depending on the case.

Actually, a variable, function argument, or function couldn’t be deleted also in ES3 (except the eval context). Here, in strict mode, delete additionally provides errors (including deleting in the eval context):

"use strict";

var foo = {};

function bar(x) {
  delete x; // SyntaxError
}

bar(10);  // SyntaxError

delete foo; // SyntaxError
delete bar; // SyntaxError

Object.defineProperty(foo, "baz", {
  value: 10,
  configurable: false
});

// but when delete a
// property, then TypeError

delete foo.baz; // TypeError

// SyntaxError
eval("var x = 10; delete x;"); // in non-strict is OK

There is another subtle case (which me mentioned in the above section devoted to undeclared assignment restriction) related with the delete operator in strict mode. According to 11.4.1 The delete Operator, it’s not possible in the strict mode to delete a binding of an environment record regardless the state of its [[Configurable]] attribute. Bindings of environment records are variables, function declarations, formal parameters or simple properties of e.g. global object. Thus, we have:

"use strict";

var foo = 10;
delete foo; // SyntaxError, [[Configurable]] == false

this.bar = 20; // OK
delete this.bar; // OK, [[Configurable]] == true

// However, if we try to delete it
// without specifying the base,
// we have again a SynaxError
// according to step 5-a of the 11.4.1

this.baz = 30; // OK
delete baz; // SyntaxError
delete this.baz; // OK

with statement

As we know a with statement in ES3 augments a scope chain of an execution context and restores it back after the evaluation. It does the same in a non-strict mode of ES5, although in the different terminology related with lexical environments which will be discussed in the following chapters.

Statement with semantically is used to reference properties of an object without specifying object’s name prefix. It also could be used as e.g. “using namespace” of C++. For example:

namespace("Widget")(function () { with (Widget) {
  init().repaint();
}});

However, because of some “magic” (but actually — just inattentive) confusions — for example, one of The Quiz’s questions, it was decided to remove with statement completely from the strict mode. Now it provides a SyntaxError:

"use strict";

// SyntaxError
with ({a: 10}) {
  alert(a);
}

this value restrictions

In the strict mode, a this value is not automatically coerced to an object. A this value of null or undefined is not converted to the global object and primitive values are not converted to wrapper objects. The this value passed via a function call (including calls made using Function.prototype.apply and Function.prototype.call) do not coerce the passed this value to an object:

"use strict";

// undefined "this" value,
// but not the global object
function foo() {
  alert(this); // undefined
}

foo(); // undefined

// "this" is a primitive
Number.prototype.test = function () {
  alert(typeof this); // number
};

1..test(); // number

foo.call(null); // null
foo.apply(undefined); // undefined

Undefined value for this can help to avoid cases with using constructors, forgetting new keyword:

// non-strict
function A(x) {
  this.x = x;
}

var a = A(10); // forget "new" keyword

// as a result "a" is undefined,
// because exactly this value is returned
// implicitly from the A function

alert(a); // undefined

// and again created "x" property
// of the global object, because "this"
// is coerced to global object in the
// non-strict in such case

alert(x); // 10

In strict mode, an exception is thrown:

"use strict";

function A(x) {
  this.x = x;
}

// forget "new" keyword,
// error, because undefined.x = 10

var a = A(10);

var b = new A(10); // OK

Indirect eval call

Sometimes, a this value in a function call — as in the example above in the foo function — is used to get the global object from any context:

function global() {
  return this;
}

Now in a strict mode such calls return undefined. To evaluate a code in the global context with exactly this value as the global object can help a so-called indirect eval call. To specify what is indirect eval call, let’s define a direct eval call concept first.

A direct call to the eval function is one that uses exactly a call expression on Reference type value which referenced name component is "eval" and which base component is an environment record.

We will discuss environment records in the following chapters, and now is just good to remind a Reference type from the the chapter 3 of ES3 series devoted to this value. The following example uses a direct call to the eval and evaluates depending on the calling context:

"use strict";

var x = 10;

// a direct call
eval("alert(x)"); // 10

// because call expression is
// applied on the value of Reference type

var evalReference = {
  base: <global environment record>,
  referencedName: "eval",
  strictReference: true // flag, that we are in strict mode
};

// inside a function context
// also can be a direct call to "eval"

(function foo() {
  var x = 20;
  // also a direct call
  eval("alert(x)"); // 20 - from the calling context
})();

In other words, if eval syntactically is called exactly via this form — eval(...), this is a direct eval call. All the other variations — are indirect calls to eval and evaluate in the global context including this value as a global object and all variable bindings from the global context. For example:

var x = 10;

(function foo() {

  var x = 20;
 
  // an indirect call, because a referenced name
  // of a Reference type is value not "eval" but "indirectEval"
 
  var indirectEval = eval;
  indirectEval("alert(x)"); // 10 - from the global context

  // also indirect, because comma returns a
  // function object, so we apply a call
  // expression not to a Reference type value at all

  ("indirect", eval)("alert(x);"); // again 10

  var window = 100;

  eval("alert(window);"); // 100 - via a direct cal
  indirectEval("alert(window);"); // object - via indirect

})();

// this is also indirect "eval", since
// the base of the Reference type value
// is the global object, and not environment record

this.eval("var y = 20;");

alert(y); // 20

So this approach can be used in the strict mode to get the global object via this value:

var x = 10;

(function foo() {

  var get = eval;
  var global = get("this");
 
  alert(this.x); // 20
  alert(global.x); // 10

}).call({x: 20});

Direct and indirect calls to eval can be used regardless the strict mode. We’ve just considered them in a view of one “issue” related with the strict mode. Generally, this separation on direct and indirect eval calls again is related with security reasons.

Notice, that using already mentioned above Function function, which evaluates its code having only global object in scope chain, in common case won’t help to get global this value in strict mode. Since, again — it just has global object as its environment record, but not the complete semantics of the global context, including this value, which in such calls can be any other object:

"use strict";

var x = 10;

(function test() {
  var foo = {
    x: 20,
    bar: Function("'use strict'; print(x); print(this.x);")
  };
  foo.bar(); // 10, 20 
})();

And regarding the issue with getting the correct value of the global object, it is possible to define a simple global constant and use this name binding from any context (although, it can be shadowed by the local variable with the same name):

Object.defineProperty(this, "global", {
  value: this
});

var x = 10;

(function foo() {

  var x = 30;
 
  alert(x); // 30
  alert(this.x); // 20
  alert(global.x); // 10

  // but var global; will cause an issue

}).call({x: 20});

From the other hand, undefined value for this in a simple function call, can be used for testing, whether we currently are in the strict mode:

"use strict";

function isStrictMode() {
  return !this;
}

if (isStrictMode()) {
  // possibly specific implementation
}

Another very important feature of the indirect eval call is that it allows to define a global binding (a variable or a function) regardless even the fact that the global code is defined as strict. E.g.:

"use strict";

 // direct "eval" call, evaluates
// in the "sandbox" environment 

eval("var a = 10;");
console.log(typeof a); // "undefined"

// indirect "eval" call which evaluates
// also always in the global context and 
// in contrast *does* affect the global 
// environment, i.e. creates the variable

this.eval("var b = 20;");
console.log(b); // 20

// show again that indirect "eval"
// creates the global binding

(function foo() {
  "use strict";

  eval("var x = 100;") // direct
  console.log(typeof x); // "undefined"

  ("indirect", eval)("var y = 200;"); // indirect
  console.log(y); // 200
})();

// and exactly global "y" is created

console.log(y); // 200

Conclusion

In this chapter we in detail discussed all features related with the “strict mode” of the ECMA-262-5 specification. There is a philosophical dilemma whether such separation on “strict” and “non-strict” is needed — but, this is already another topic. As always, if you have questions or additions, we can discuss them in comments.

Additional literature


Written by: Dmitry A. Soshnikov.
Published on: 2010-06-01

Write a Comment

Comment

21 Comments

  1. Надеюсь появится перевод на русский, потому как не совсем все понятно к сожалению. К примеру это:

    var get = eval;
    get("alert(x)"); // 10 - from the global context

    да и вобще это было единственным местом, где про js можно было почитать на русском и подробно.

  2. Hi,

    Thanks for the article!

    While it’s all nice in theory, I’d be useful to know which browsers support this strict mode.

    Thanks!

  3. @Sergey

    Надеюсь появится перевод на русский

    Пока, к сожалению, не планирую; возможно, позже — когда допишу все части этой новой серии. Но, этим может заняться кто-нибудь другой. Я смогу проверить качество перевода в техническом плане (относительно самого JS имеется в виду); отсюда смогу дать ссылки, как это сделано с китайскими переводами, например.

    потому как не совсем все понятно к сожалению. К примеру это:

    var get = eval;
    get("alert(x)"); // 10 - from the global context

    Суть в том, что есть явный (direct) и неявный (indirect) вызовы eval’a. Вариации неявного вызова описаны в статье; одна из них — это использовать другое имя идентификатора. Главный момент, это то, что неявный вызов исполняется всегда в глобальном контексте, поэтому “x” из примера выше берётся именно оттуда, а не из локального контекста функции.

    да и вобще это было единственным местом, где про js можно было почитать на русском и подробно.

    Ну, все русские (оригинальные) статьи серии ES3 по-прежнему здесь. Возможно, появятся из переводы ES5 серии.

    Dmitry.

  4. @Art

    I’d be useful to know which browsers support this strict mode

    Currently, the only complete implementation of ES5 is the BESEN engine. I’ve updated the article, mentioning it. It still has some bugs, several of them I’ve posted during writing the article — and glad to see that the implementer has already fixed them. You can download it and test ES5 there now. Hope, some major implementations will follow soon.

    Dmitry.

  5. Awesome overview! 🙂

    But a couple of important corrections:

    1. “eval” and “arguments” not being able to occur as properties — e.g. `({ eval: 1 })` — is a misconception. From what I can see, there’s nothing in the specs that disallows it. The confusion probably comes from the fact that “eval” and “arguments” can NOT occur as /Identifier/ in /PropertySetParameterList/ — `({ set foo(eval, arguments) { } }) /* SyntaxError */` By the way, IIRC, last time I checked, BESEN was incorrectly throwing an error on `({ eval: 1 })`.

    2. Use Strict Directive (being an /ExpressionStatement/ in Directive Prologue) can NOT occur anywhere in the scope of a function (or program body), contrary to what you say. Well, technically it can, but then it’s just an /ExpressionStatement/ consisting of a string literal, not a Use Strict Directive. The special nature of /ExpressionStatement/’s in Directive Prologue is that they occur as the _initial /SourceElement/’s_ in function or program. So `alert(1); “use strict”` is not a strict-mode code, but `”use strict”; alert(1);` is.

  6. @kangax

    Yeah, good points, thanks.

    Regarding both — these were bugs in BESEN. The second one (with “use strict” position) was fixed earlier, but after the article was written. However, the spec is clear in this case — it is an initial SourceElement.

    The first one is not so easy to catch in spec, but, yeah, obviously, it was also a bug in BESEN, and the spec is also clear here. I’ve talked to BeRo just some minutes ago, it’s fixed now.

    Dmitry.

  7. In the Strictness Scope section you should probably also discuss that a direct eval in strict code treats the eval code as strict code and that all other evals treat the eval code as non-strict code unless the eval code contains an use strict directive.

  8. Hey Dimitry,

    fantastic article. Though I have a question, how the strict mode is handled throughout different files:

    <script src="a.js">
    <script src="b.js">
    <script>
    var mode = 42; // which mode is this here?
    </script>

    a.js:

    var foo = "bar";

    b.js:

    "use strict";
    var bar = "baz";

    In which mode is the rest of the script on this demo? Does the script element encapsulate the strict mode in any way or is it passed through?

    Thanks
    gossi

  9. @gossi

    Does the script element encapsulate the strict mode in any way or is it passed through?

    Yes, the strict mode is own per each script element (i.e for each loaded file or evaluated source inside it.

    Actually, script element is a separate program, though all these programs share the same global object.

    <script>
      "use strict";
      var a = 10; // OK, no error
    </script>
    
    <script>
      b = 20; // OK, no error
    </script>
    
    <script>
      "use strict";
      b = 20; // Error
    </script>

    Dmitry.

  10. […] eval and arguments identifiers are not allowed for assignment, function argument names or property names […]

    Why wouldn’t they be allowed for property names? The spec only mentions the following:

    It is a SyntaxError if a VariableDeclaration or VariableDeclarationNoIn occurs within strict code and its Identifier is either eval or arguments.

    So, I don’t see why this would throw an error:

    (function() {
      'use strict';
      var object = {
        eval: 1,
        arguments: 2
      };
    }());
    

    Could you please clarify? Thanks!

  11. Спасибо за такую подробную статью.

    Не совсем понял про delete локальных переменных:

    (function () {
        "use strict";
        var a = 1;
        delete a; // будет ли исключение?
    })();
    

    И есть ли в ES5 какие-нибудь изменения про обязательность ; в конце строк?

  12. @Yuriy

    Да, в случае delete‘a исключение будет, т.к. var создает non-[[Configurable]] свойство (только если это не контекст eval‘a).

    По поводу ASI (Automatic Semicolon Insertion) — нет, точка с запятой все еще дополняется автоматом. Хотя, были дебаты, что могли бы убрать этот механизм в строгом режиме. С другой стороны, точки с запятыми тоже можно отнести к “синтаксическому мусору” в языке. Однако все ставят в крупных проектах из-за порой сложных правил ASI.

  13. --script 1
    window.a() //error
    window.b() // work
    --script 2
    ‘use strict’
    function a(){}
    --script3
    function b(){}
  14. After all these years, your blog is still the top and exclusive resource for learning the actual javascript.

    Thanks so much for that.

    P. S. typo: in the last example, the last 2 `console.log`s should print out `200` instead of `20`.

  15. “use strict”;
    undeclared = (this.undeclared = 10, alert(undeclared), 10);

    Run the above codes in the latest browsers, i.e. Chrome, Edge, it does not show any error. What happens? I thought it should show some errors.
    Thank you.