Strict mode in JavaScript

Strict mode in JavaScript
trict mode is an important part of modern JavaScript. It is a mode that lets us opt-in to a more restricted syntax of JavaScript.

Strict mode is an important part of modern JavaScript. It is a mode that lets us opt-in to a more restricted syntax of JavaScript.

The semantics of strict mode is different from the old “sloppy mode” of JavaScript that has a looser syntax and makes errors in code that are silenced. This means that errors are ignored and the code might run with unexpected results.

Strict mode makes several changes to JavaScript semantics. It eliminates silent errors and instead throws them so that the code won’t run with errors in the code.

It will also point out mistakes that prevent JavaScript engines from doing optimizations. Also, it prohibits features that are likely to be defined in future versions of JavaScript.

Strict mode can be applied to individual functions or the whole script. It can’t be applied only to statements or other blocks enclosed in curly braces. To make a script use strict mode, we add the statement "use strict" or 'use strict' at the top of the script before any other statements.

If we have some scripts that use strict mode and others that don’t, then we might have scripts that use strict mode that are concatenated with other scripts that don’t use strict mode.

This means that code that doesn’t use strict mode might have been made to use strict mode when it’s concatenated together. The reverse is also true. So, it’s best not to mix them together.

We can also apply this to functions. To do this, we add the statement "use strict" or 'use strict' inside the functions on the top of the body, before any other statements. It’s applied to everything inside, including functions that are nested in the function that uses strict mode.

For example:

const strictFunction = ()=>{
  'use strict';
  const nestedFunction = ()=>{
    // this function also uses strict mode
  }
}

JavaScript modules, introduced in ES2015, have strict mode automatically enabled, so no statement is needed to enable it.

Changes in Strict Mode

Strict mode changes both the syntax and run time behavior of code that uses it. Mistakes in code are converted to errors as syntax errors that are thrown in run time, simplify how particular variables are computed, changes that simplify the eval function and the arguments object, and changes that might be implemented in future ES specification.

Converting Mistakes to Errors

Mistakes are converted to errors. They were previously accepted in non-strict mode. Strict mode restricts the use of error syntax and will not let the code run with errors in place.

It makes creating global variables hard by not letting us declare variables with var, let, or const, so creating variables without declaring them with those keywords won’t work. For example, the following code will throw a ReferenceError:

'use strict';
badVariable = 1;

We can’t run the code above in strict mode because this code will create a global variable badVariable if strict mode is off. Strict mode prevents this to prevent the creation of global variables accidentally.

Any code that silently fails will now throw an exception. This includes any invalid syntax that was ignored silently before.

For example, we can’t not assign to read-only variables like arguments, NaN, or eval with strict mode on.

Any assignment to read-only properties like non-writable global properties, assignment of getter-only properties, and assigning things to properties on non-extendable objects will throw an exception in strict mode.

Below are some examples of syntax that will fail with strict mode on:

'use strict';

let undefined = 5; 
let Infinity = 5;

let obj = {};
Object.defineProperty(obj, 'foo', { value: 1, writable: false });
obj.foo = 1

let obj2 = { get foo() { return 17; } };
obj2.foo = 2

let fixedObj = {};
Object.preventExtensions(fixedObj);
fixed.bar= 1;

All of the examples above will throw a TypeError. undefined and Infinity are non-writable global objects. obj is a non-writable property.

obj2’s foo property is a getter only property and so cannot be set. fixedObj was prevented from adding more properties to it with the Object.preventExtensions method.

Also, deleting undeletable properties will throw a TypeError when there is code attempting to do so. For example:

'use strict';
delete Array.prototype

This will throw a TypeError.

Strict mode also disallows duplicate property names in an object, so the following example will throw a syntax error:

'use strict';
let o = { a: 1, a: 2 };

Strict mode requires that function parameter names are unique. Without strict mode, if two parameters have the name one, then the one defined later will be accepted as the parameter’s value when arguments are passed in.

With strict mode, having multiple function parameters with the same name is no longer allowed, so the following example will fail to run with a syntax error:

const multiply = (x, x, y) => x*x*y;

Octal syntax is also not allowed in strict mode. It isn’t part of the specification, but it’s supported in browsers by prefixing octal numbers with a 0.

This confuses developers as some may think that the 0 preceding the number is meaningless. Therefore, strict mode disallows this syntax and will throw a syntax error.

Strict mode also prevents the use of syntax that makes optimizations difficult. It needs to know that a variable is actually stored at the location that it thinks it’s stored at before doing optimization, so we have to prevent the kind of syntax that stops optimizations from happening.

One example of this is the with statement. If we use it, it prevents the JavaScript interpreter from knowing which variable or property you’re referring to, as it’s possible to have a variable with the same name inside or outside the with statement.

If we have something like the following code:

let x = 1;
with (obj) {
  x;
}

Then, JavaScript wouldn’t know if the x inside the with statement is referring to the x variable or the property of obj, obj.x.

Therefore, the memory location of x is ambiguous. So, strict mode prevents the with statement from being used. If we have strict mode like the following:

'use strict';
let x = 1;
with (obj) {
  x;
}

Then the code above will have a syntax error.

Another thing that strict mode prevents is the declaration of variables inside an eval statement.

For example, without strict mode, eval('let x') would declare the variable x into the code. This lets people hide variable declarations in strings that might override the same variable declaration that’s outside the eval statement.

To prevent this, strict mode disallows variable declarations in the string argument that we pass into an eval statement.

Strict mode also prohibits deletion of plain variable names, so the following will throw a syntax error:

'use strict';

let x;
delete x;

Prohibiting Invalid Syntax

Invalid syntax of eval and argument isn’t allowed in strict mode.

This means that any manipulation done to them that isn’t allowed, like assigning new values to them or using them as names of variables, functions, or parameters in functions.

The following are examples of invalid uses of eval and the argument object that isn’t allowed:

'use strict';
eval = 1;
arguments++;
arguments--;
++eval;
eval--;
let obj = { set p(arguments) { } };
let eval;
try { } catch (arguments) { }
try { } catch (eval) { }
function x(eval) { }
function arguments() { }
let y = function eval() { };
let eval = ()=>{ };
let f = new Function('arguments', "'use strict'; return 1;");

Strict mode doesn’t allow an alias for the arguments object to be created and set with new values via the alias.

Without strict mode, if the first parameter of a function is a, then setting a also sets arguments[0]. With strict mode, the arguments object will always have the list of arguments that the function is called with.

For example, if we have:

const fn = function(a) {
  'use strict';
  a = 2;
  return [a, arguments[0]];
}
console.log(fn(1))

Then we should see [2,1] logged. This is because setting a to 2 doesn’t also set arguments[0] to 2.

Performance Optimizations

Also, there’s no more support for arguments.callee. Without strict mode, all it does is return the name of the function that’s being called that the arguments.callee is in.

It prevents optimizations like inline functions because arguments.callee requires that a reference to the un-inlined function is available if arguments.callee is accessed. Therefore, with strict mode, arguments.callee now throws a TypeError.

With strict mode, this will not be forced into always being an object. If a function’s this is bound, with call, apply, or bind to any non-object type, like primitive types of undefined, null, number, boolean, etc, then they must be forced into being objects.

If the context of this is switched to a non-object type, then the global window object will take its place. This means that the global object is exposed to the function that’s being called with this being bound to non-object types.

For example, if we run the following code:

'use strict';
function fn() {
  return this;
}
console.log(fn() === undefined);
console.log(fn.call(2) === 2);
console.log(fn.apply(null) === null);
console.log(fn.call(undefined) === undefined);
console.log(fn.bind(true)() === true);

All the console logs will be true, as the this inside the function isn’t automatically converted into the window global object when this is changed to something that has a non-object type.

Security Fixes

In strict mode, we also don’t allow the function’s caller and arguments to be exposed to the public since caller may expose the function that calls the function that the function’s caller property accessed.

The arguments have arguments that are passed in when the function is called. For example, if we have a function called fn, then through fn.caller, we can see the function that called fn, and through fn.arguments, we can see the arguments that were passed into fn when the call to fn is made.

This is a potential security hole that is eliminated by prohibiting access to these two properties of the function.

function secretFunction() {
  'use strict';
  secretFunction.caller;    
  secretFunction.arguments;
}
function restrictedRunner() {
  return secretFunction();
}
restrictedRunner();

In the example above, we can’t access secretFunction.caller and secretFunction.arguments with strict mode as people may use it to get the call stack of functions. A TypeError will be thrown if we run that code.

Identifiers that will become restricted keywords in future versions of JavaScript will not be allowed to be used for identifiers like variable or property names.

The following keywords won’t be allowed to be used for defining our identifiers in code. implements, interface, let, package, private, protected, public, static, and yield.

With ES2015 or later, these have become reserved words, so they definitely cannot be used for naming variables and object properties without strict mode.

Strict mode has been a standard for a few years. Browser support for it is common, and only old browsers like Internet Explorer may have problems with it.

Other browsers shouldn’t have problems with working with strict mode. Therefore, it should be used to prevent mistakes and avoiding security hazards like exposing call stacks or declaring new variables in eval.

Also, it eliminates silent errors and instead throws them so that the code won’t run with errors in the code. It will also point out mistakes that prevent JavaScript engines to do optimizations.

Also, it prohibits features that are likely to be defined in future versions of JavaScript.

Recommended Reading

What Are the New Features of Angular 9?

An introduction to Machine Learning with Brain.js

Suggest:

JavaScript Programming Tutorial Full Course for Beginners

Learn JavaScript - Become a Zero to Hero

Javascript Project Tutorial: Budget App

Top 10 JavaScript Questions

E-Commerce JavaScript Tutorial - Shopping Cart from Scratch

JavaScript for React Developers | Mosh