JavaScript strict mode restrictions

In the previous post we took a look at what strict mode was all about. Today we review some of the key restrictions that become applicable when strict mode is active.

  1. Identifiers must be declared before they can be assigned to

    This aspect in my opinion makes strict mode worthwhile all on its own even if there was nothing more to it. Undeclared variable assignments do not automatically get added as expando properties on the global object.

    "use strict";
    foo = 10; // ReferenceError: Variable undefined in strict mode
    

    With this in place, the following snippet that I'd given in the first post under the "What is strict mode?" section will fail to run and throw a "ReferenceError" instead because of the typo in the variable name "product" in the assignment inside the "for" loop.

    function findProduct(numbers) {
        "use strict";
        var product = 0,
            len = numbers.length;
        for(var i = 0; i < len; ++i) {
            prodct = product * numbers[i]; // ReferenceError: Variable undefined in strict mode
        }
        return product;
    }
    
  2. No automatic context for context-less function calls

    Functions that are called without setting an explicit context do not automatically get the "global object" in "this". Consider the following snippet:

    function foo() {
        // prints "true"
        print(this === window);
    }
    foo();
    

    Here, "foo" is being invoked without setting an explicit context object, i.e., we are not calling it like so:

    foo.call({foo: "bar"});
    

    In unrestricted mode this causes the context to be automatically initialized to the "global" object, which in browsers is the "window" object. Since the snippet above was running in unrestricted mode, the expression "this === window" evaluates to true. If we modify the function like so however, we see that "this" is no longer equal to "window":

    function foo() {
        "use strict";
        // prints "false"
        print(this === window);
    }
    foo();
    
  3. Reserved keywords cannot be identifier names

    Naming variables and functions as eval, arguments, implements, let, private, public, yield, interface, package, protected and static will cause errors.

    "use strict";
    var yield; // SyntaxError: Expected identifier
    
  4. Violations of ES5 property configuration cause errors

    Violations of the configuration as specified in the property descriptor for ES5 properties will cause errors to be thrown in strict mode instead of them being silently ignored. Here are some examples:

    1. Writing to a non-writable property:

      "use strict";
      var person = Object.create({}, {
          name: {
              value: "foo",
              writable: false,
              configurable: true,
              enumerable: true
          }
      });
      
      
      // TypeError: Assignment to read only properties not allowed in 
      // strict mode
      person.name = "bar";
      

      Note the line highlighted in bold. Setting the "writable" property descriptor to "false" makes the "name" property of the "person" object read-only. Attempts to assign to this property will be silently ignored in unrestricted mode but causes a "TypeError" to be thrown in strict mode.

    2. Changing configuration of a non-configurable property:

      "use strict";
      var person = Object.create({}, {
          name: {
              value: "foo",
              writable: false,
              configurable: false,
              enumerable: true
          }
      });
      
      
      // TypeError: Cannot redefine non-configurable property 'name'
      Object.defineProperty(person, "name", {
          value: "bar",
          writable: true,
          configurable: true,
          enumerable: true
      });
      

      Here we attempted to change the property descriptor on a non-configurable object. Again, an error that would have gone unnoticed in unrestricted mode results in a "TypeError" being thrown in strict mode.

  5. Writing to read-only accessor properties

    Writing to accessor properties that do not have a "setter" defined causes errors to be thrown instead of being silently ignored:

    "use strict";
    var person = Object.create({}, {
        name: {
            get: function() {
                return "foo";
            },
            configurable: false,
            enumerable: true
        }
    });
    
    
    // TypeError
    person.name = "bar";
    

    Here, "name" is an accessor property that does not have a "setter" method defined. Attempts to assign a value to this property results in an error in strict mode while being quietly ignored in unrestricted mode.

  6. Cannot extend non-extensible objects

    Extending a non-extensible object throws an error in strict mode instead of being silently ignored:

    "use strict";
    var person = {
        name: "foo"
    };
    Object.preventExtensions(person);
    
    
    // TypeError: Cannot create property for a non-extensible object
    person.age = 10;
    
  7. Other sundry restrictions

    There are a few other sundry restrictions for strict mode code which are perhaps somewhat less frequently used. I have briefly reviewed them below:

    1. Numeric constants are no longer interpreted as having an octal base if you put a leading zero.

    2. Variable/function instantiations in strict mode "eval" code occur in an environment that is local to the "eval" code and not in the environment of the calling code. This means that "eval" code cannot introduce new identifiers in the caller's execution context/scope.

    3. "arguments" is immutable, i.e., you cannot arbitrarily extend the "arguments" object by tacking on your own properties to it. Now why anyone would even want to do this is puzzling to me but it must have been happening often enough for Ecma to take the effort to specify that you can no longer do it in strict mode!

    4. "arguments.callee" and "arguments.caller" are not available in strict functions. I cannot say that I am completely happy with this particular restriction!

    5. Creating duplicate property definitions on an object is not allowed in strict mode code. The following snippet for instance, produces an error in strict mode:

      "use strict";
      var o = Object.create({}, {
          name: {
              value: "foo"
          },
          name: {
              value: "bar"
          }
      });
      

      This code throws up a "SyntaxError" with the message, "Multiple definitions of a property not allowed in strict mode". In unrestricted mode, "o.name" would have the value "bar".

    6. Calling "delete" on an ES5 property which has its "configurable" property set to "false" results in an error being thrown in strict mode. This is a variation on the restriction discussed in point 4 above.

    7. The JavaScript "with" statement is not allowed in strict mode code.

    8. It is not allowed to create functions with duplicate parameter names in strict mode. Again, it defies logic as to why anyone would want to do this but there it is!

Some best practices

Here are some things that you should perhaps consider while writing code that uses strict mode:

  1. Given that "strict mode" is a fairly recent phenomenon, unless all your users have been conscientiously updating their favorite browser to a new version, chances are high that you will have some percentage of visitors to your web app who are running a JS engine that does not understand "strict mode". This means that basically all of your code is going to run in unrestricted mode regardless of whether or not you've used the strict mode directive. It becomes important therefore to always test your code in unrestricted mode and make sure that everything is still peachy.

  2. Now, even if a major chunk of your end users are on browsers that don't support strict mode it still makes sense to use strict mode in your development environment because a large part of strict mode is about enforcing JavaScript development best practices and you can benefit from automated enforcement at least during development. Just make sure that you test everything in unrestricted mode before going live!

  3. Enable strict mode in small increments instead of a one-shot quick fix. If you have a 3000 line JS file for instance, it is probably not wise to simply add the "use strict"; directive at the top of the file as the semantic differences between the two modes can potentially result in subtle unforeseen bugs. Do it in smaller chunks at a function level and certainly use it with abandon for new code that you write.

I'd encourage you to experiment and read up on strict mode. In my opinion, this is one of the best features introduced as part of ES5. Here are some resources that you are likely to find useful:

With that we come to a close of the two part series on JavaScript strict mode. What do you folks think about this feature? Feel free to let me know what you think by leaving a comment.

comments powered by Disqus