Nerdworks logo "The nerd shall inherit the earth."

Nerdworks Blogorama

Nerdspeak

On the HTML5 Indexed DB API - Part 4 of n
Technobabble
9/8/2011 3:51:02 AM  

So far we have looked at creating databases and adding data to them. Let's next take a look at retrieving records from the database. Surprisingly, this turns out to be a bit complex. The indexed DB way of enumerating records from an object store is to use a “cursor” object. A cursor can iterate over records from an underlying object store or an index. A cursor has the following key properties:

  1. A range of records in either an index or an object store.
  2. A source that references the index or object store that the cursor is iterating over.
  3. A position indicating the current position of the cursor in the given range of records.

While the concept of a cursor is fairly straightforward, writing the code to actually iterate over an object store turns out to be somewhat tricky given the asynchronous nature of all the API calls. Let’s implement the listNotes method of our NotesStore object and see what the code looks like.

...
listNotes: function (callback) {
    var self = this,
        txn = self.db.transaction(null, TransactionMode.ReadOnly),
        notes = [],
        store = txn.objectStore(self.store_name);

    Utils.request(store.openCursor(), function (e) {
        var cursor = e.result,
            iterate = function () {
                Utils.request(cursor.move(), function (e2) {
                    // if "result" is true then we have data else
                    // we have reached end of line
                    if (e2.result) {
                        notes.push(cursor.value);

                        // recursively get next record
                        iterate();
                    }
                    else {
                        // we are done retrieving rows; invoke callback
                        txn.commit();
                        callback(notes);
                    }
                });
            };

        // set the ball rolling by calling iterate for the first row
        iterate();
    });
},
...

Let’s break this implementation down:

  1. First we acquire a transaction object by calling the database object’s transaction method. Note that this time we are indicating that we require a “read-only” transaction.
  2. Next we retrieve a reference to the object store via the objectStore method of the transaction object.
  3. Then we issue an async call to the openCursor API on the object store. The tricky part here is that every single iteration over a record in the cursor is itself an async operation! To prevent the code from drowning in a sea of callbacks we define a local function called iterate to encapsulate the logic of iterating over every record in the cursor.
  4. This iterate function makes an async call to the cursor object’s move method and recursively invokes itself again in the callback if it detects that there are more rows to be retrieved. Once all the rows in the cursor have been retrieved we finally invoke the callback method passed by the caller handing in the retrieved data as a parameter.

That’s it for today’s post. I’ll let you mull over this for a bit till the next post when we’ll cover some more scenarios.

Link Comment
 
On the HTML5 Indexed DB API - Part 3 of n
Technobabble
9/6/2011 12:43:56 AM  

In the previous post we covered some more background on working with the Indexed DB API and briefly reviewed the mechanics of creating a new database. In this post we take a look at some important Indexed DB constructs that one must be familiar with while working with the core APIs.

Object stores

Object stores are the indexed DB equivalent of "tables" from the relational database world. All data is stored inside object stores and serves as the primary unit of storage. A database can contain multiple object stores and each store is a collection of records. Each record is a simple key/value pair. Keys must uniquely identify a particular record and can be auto-generated. The records in an object store are automatically sorted in ascending order by keys. And finally, object stores can be created and deleted only under the context of "version change" transactions. We’ll see what a "version change" transaction is when we review transactions later on in this post.

Keys and Values

Each record in the object store is uniquely identified by a "key". Keys can be arrays, strings, dates or numbers. For the purpose of comparison, arrays are considered to be greater than strings which are greater than dates which in turn are considered to be greater than numbers. Keys can be "in-line" keys or not. By "in-line" we indicate to indexed DB that the key for a particular record is actually a part of the value object itself. In our notes store sample for instance, each note object has an id property which contains the unique identifier for a particular note. This is an example of an "in-line" key, i.e., the key is a part of the value object.

Whenever keys are "in-line", we must also specify a "key path", i.e. a string that signifies how the key value can be extracted from the value object. The key path for "note" objects for instance is the string "id" since the key can be extracted from note instances by accessing the "id" property. But this scheme allows for the key value to be stored at an arbitrary depth in the value object’s member hierarchy. Consider the following sample value object:

var product = {
  info: {
    name: "Towel",
    type: "Indispensable hitchhiker item",
  },
  identity: {
    server: {
      value: "T01"
    },
    client: {
      value: "TC01"
    },
  },
  price: "Priceless"
};

Here, the following key path might be used:

identity.client.value

Database versioning

Indexed DB databases have a version string associated with them. This can be used by web applications to determine whether the database on a particular client has the latest structure or not. This is useful when you make changes to your database’s data model and want to propagate those changes to existing clients who are on the previous version of your data model. You can simply change the version number for the new structure and check for it the next time the user runs your app and do the needful to upgrade the structure and migrate the data.

Version number changes must be performed under the context of a "version change" transaction. Before we talk about that though let’s quickly review what "transactions" are.

Transactions

Like relational databases, indexed DB also performs all of its I/O operations under the context of transactions. Transactions are created through connection objects and enable atomic, durable data access and mutation. There are two key attributes for transaction objects:

  1. Scope

    The scope determines which parts of the database can be affected through the transaction. This basically helps the indexed DB implementation determine what kind of isolation level to apply to objects during the lifetime of the transaction. You can think of the scope as simply being a list of object stores that will form a part of the transaction.

  2. Mode

    The transaction mode determines what kind of I/O operation is permitted in the transaction. The mode can be:

    1. Read only

      Allows only "read" operations on the objects that are a part of the transaction’s scope.

    2. Read/write

      Allows "read" and "write" operations on the objects that are a part of the transaction’s scope.

    3. Version change

      The "version change" mode allows "read" and "write" operations and also allows the creation and deletion of object stores and indexes, i.e., the structure of the database can be modified only under the context of a "version change" transaction.

    Transaction objects auto-commit themselves unless they have been explicitly aborted. Transaction objects expose events to notify clients of:

    • when they complete
    • when they abort and
    • when they timeout

Creating the object store

Our notes store database will contain only a single object store to record the list of notes. As discussed earlier, object stores must be created under the context of a "version change" transaction. Let’s go ahead and extend the init method of the NotesStore object to include the creation of the object store. I’ve highlighted the changed bits in bold.

var NotesStore = {
    name: "notes-db",
    store_name: "notes-store",
    store_key_path: "id",
    db: null,
    ver: "1.0",
    init: function (callback) {
        var self = this;
        callback = callback || function () { };
        Utils.request(window.indexedDB.open("open", this.name), function (e) {
            self.db = e.result;

            // if the version of this db is not equal to
            // self.version then change the version
            if (self.db.version !== self.version) {
                Utils.request(self.db.setVersion(self.ver), function (e2) {
                    var txn = e2.result;

                    // create object store
                    self.db.createObjectStore(self.store_name,
                                              self.store_key_path,
                                              true);
                    txn.commit();
                    callback();
                });
            } else {
                callback();
            }
        });
    },
    …

Object stores are created by calling the createObjectStore method on the database object. The first parameter is the name of the object store. This is followed by the string identifying the key path and finally a Boolean flag indicating whether the key value should be auto-generated by the database when new records are added.

Adding data to object stores

New records can be added to an object store by calling the put method on the object store object. A reference to the object store instance can be retrieved through the transaction object. Let’s implement the addNote method of our NotesStore object and see how we can go about adding a new record:

    …
    addNote: function (text, tags, callback) {
        var self = this;
        callback = callback || function () { };
        var txn = self.db.transaction(null, TransactionMode.ReadWrite);
        var store = txn.objectStore(self.store_name);
        Utils.request(store.put({
            text: text,
            tags: tags
        }), function (e) {
            txn.commit();
            callback();
        });
    },

    …

This method can be broken down into the following steps:

  1. Invoke the transaction method on the database object to start off a new transaction. The first parameter is the list of names of the object stores which are going to be a part of the transaction. Passing null causes all the object stores in the database to be a part of the scope. The second parameter indicates the transaction mode. This is basically a numeric constant which we have declared like so:

    // IndexedDB transaction mode constants
    var TransactionMode = {
        ReadWrite: 0,
        ReadOnly: 1,
        VersionChange: 2
    };
  2. Once the transaction has been created we acquire a reference to the object store in question through the transaction object’s objectStore method.

  3. Once we have the object store handy, adding a new record is just a matter of issuing an asynchronous API call to the object store’s put method passing in the new object to be added to the store. Note that we do not pass a value for the id field of the new note object. Since we passed true for the auto-generate parameter while creating the object store, the indexed DB implementation should take care of automatically assigning a unique identifier for the new record.

  4. Once the asynchronous put call completes successfully, we commit the transaction.

Coming up

In the next post, we’ll take a look at retrieving records from an indexed DB. Surprisingly, this turns out to be slightly trickier than what one might expect.

Link Comment
 
On the HTML5 Indexed DB API - Part 2 of n
Technobabble
9/4/2011 11:48:03 PM  

In the previous post in this series we covered the fundamentals of the Indexed DB specification. We continue the journey in this post and take a look at the API itself.

Building an offline note taking app

For the rest of this series of posts, we’ll try and build the client side data layer for a fictitious note taking web app. From a data model point of view it’s about as simple as it can get. The app allows users to write text notes and tag them with specific key words. Each note will have a unique identifier which will serve as its key and apart from the note text, it will be associated with a collection of tag strings. Here’s a sample note object represented in JavaScript object literal notation:

var note = {
      id: 1,
      text: "Note text.",
      tags: ["sample", "test"]
    };

We’ll build a NotesStore object that has the following interface:

var NotesStore = {
        init: function(callback) {
        },

        addNote: function(text, tags, callback) {
        },
    
        listNotes: function(callback) {
        }
    };

It should be fairly self-evident as to what each method does. All method calls execute asynchronously and where a result is to be returned to the caller, the interface accepts a reference to a callback that is to be invoked with the result. Let’s see what it takes to efficiently implement this object using an indexed database.

Testing for Indexed DB

The root object that you deal with when talking to the indexed DB API is called indexedDB. In fact you can check for the presence of this object to see whether the current browser supports indexed DB or not. Like so:

if(window[“indexedDB”] === undefined) {
      // nope, no indexed DB!
    } else {
      // yep, we’re good to go!
    }

Alternatively, you can use the Modernizr JavaScript library to test for support for indexed DB like so:

if(Modernizr.indexeddb) {
        // yep, go indexeddb!
    } else {
        // bleh! No joy!
    }

Asynchronous requests

The asynchronous API calls work through what are known as “request” objects. When an asynchronous API call is made, it would return a reference to a “request” object which exposes two events – onsuccess and onerror. The former is raised when the API executes successfully and the latter is raised when it errors out. Here’s what a typical call looks like:

var req = someAsyncCall();
    req.onsuccess = function() {
        // handle success case
    };
    req.onerror = function() {
        // handle error
    };

As you work with the indexedDB API you eventually get to a point where it becomes somewhat hard to keep track of all the callbacks. In a future post I’ll explore some ideas I am currently playing with on how we can simplify this further. For now though, to make our exploration of the API somewhat simpler I’ll define and use a small utility routine that abstracts the “request” pattern away:

var Utils = {
        errorHandler: function(cb) {
            return function(e) {
                if(cb) {
                    cb(e);
                } else {
                    throw e;
                }
            };
        },
    
        request: function (req, callback, err_callback) {
            if (callback) {
                req.onsuccess = function (e) {
                    callback(e);
                };
            }
            req.onerror = errorHandler(err_callback);
        }
    };

Now, I can write my async calls like so:

Utils.request(someAsyncCall(), function(e) {
        // handle completion of call
    });

Creating and opening the database

Creating/opening a database is done by calling the open method of the indexedDB object. Here’s an implementation of the NotesStore object’s init method:

var NotesStore = {
    name: “notes-db”,
    db: null,
    ver: “1.0”,
    init: function(callback) {
        var self = this;
        callback = callback || function () { };
        Utils.request(window.indexedDB.open(“open”, this.name), function(e) {
            self.db = e.result;
            callback();
        });
    },

    …

The open method opens the database if it already exists or creates a new one if not. The success callback of the request object returned by open receives an event object as a parameter whose result property is a reference to the newly opened database. You can think of this as the object that represents the connection to the database. When this object is destroyed (or if you called its close method) the connection to the database is terminated.

Coming up

Now that we have the database created, in the next post, we’ll go ahead and create the rest of the database objects.

Link Comment (4)
 
On the HTML5 Indexed DB API – Part 1 of n
Technobabble
9/3/2011 9:40:34 PM  

Over the years the web has increasingly transformed from being just a repository of content to a marketplace of full-fledged functional applications. The suite of technologies that are bandied about these days under the “HTML5” banner have as a fundamental goal, the enablement of the building of this new breed of software. In this post (and over the next post or two) we review a technology that solves an important piece of the application puzzle – that of managing storage and retrieval of user specific data on the client side, called “Indexed DB”. For almost as long as the web has been around, there has been a need for web sites to track visitor specific information in one form or another. Using HTTP cookies has so far been pretty much the only standards based option available to web developers. While plug-in based alternatives in the form of Silverlight and Flash do exist, the typical challenges imposed on end users who either do not have the plug-in in question installed or are using the wrong version serves to complicate matters significantly. Let’s first briefly review what the available alternatives are for a web developer seeking to solve the problem of managing client side storage today:

HTTP Cookies

HTTP cookies allow web applications to store small amounts of textual data on the client side using specially crafted HTTP headers. Access to this data is provided via the browser DOM. JavaScript code must be used to parse and make sense of the data. While this works, there are significant challenges in working with cookies:

  • Cookies are not allowed to be greater than 4 KB in size.

  • Cookies are transmitted with every request that the browser makes – even when it is requesting for things like images, script files or style sheets.

  • The programming model is not straightforward in that it is basically just text and you need to write code to parse and crack data stored in cookies.

  • Only textual data can be stored and retrieved.

Plug-ins

Plug-ins have historically been the natural (if not the only) choice for web developers who have needs that are not catered to sufficiently by cookies. Technologies such as Silverlight, Flash and Google Gears come with their own brand of local storage options. While they are a better option as compared to cookies they do however present some unique challenges of their own:

  • As stated earlier, the primary issue is that user experience is less than stellar when you encounter cases where the user either does not have the plug-in in question installed or has the wrong version installed and must therefore upgrade. Often users are logged in to their computers with user accounts that do not have the required permission to install new software and hence are simply locked out of the web application or functionality completely.

  • Web developers must familiarize themselves with the specific tool stack associated with the plug-in.

DOM Storage

DOM storage is a part of the HTML5 suite of specifications and is meant to be a replacement for HTTP cookies. This is actually a viable client side storage option. The idea is to basically provide a key/value based storage API that allows web applications to store user specific data in a persistent manner. The API is simple to use and it just works! However, there are some limitations to how it can be put to use which are to a large extent deliberate and by design:

  • You can only store strings in DOM storage, i.e., both key and value must be JavaScript strings. You can however use JSON serialization to manage the storage and retrieval of objects.

  • Data stored in DOM storage is scoped by the domain, i.e., it will not be possible for code running in one domain to access DOM storage data stored under the context of a different domain. While this is a good thing, the limitation however is that it is not possible to scope this with a greater level of granularity – say by specific URLs within a domain.

  • There is no mechanism to iterate through all the data stored in DOM storage. You can only request the value for a specific key that you already know.

With that brief overview of the alternatives let us move on to the topic of this post – the Indexed Database API.

What is Indexed DB?

An Indexed DB is basically a persistent data store in the browser – a database on the client side. It defines an API that allows the storage and retrieval of key/value data. Like regular relational databases, it maintains indexes over the records it stores and developers use the Indexed DB JavaScript API to locate records by key or by looking up an index. Each database is scoped by “origin”, i.e. the domain of the site that creates the database. The database is transactional and provides a means for traversing the data in a deterministic order.

The Indexed DB API includes a synchronous and an asynchronous version of pretty much all the interfaces. The asynchronous API is meant to be used from web apps when you are interacting with the database directly from the UI thread where having an API call block on an I/O operation is not acceptable. All blocking calls work asynchronously and results are reported via callbacks. The synchronous API counterparts are identical to the asynchronous versions except for the fact that there are no callbacks and results are returned directly.

A word on the status of the spec

The Indexed DB specification is currently (i.e., when this post was written) in draft status and should not be considered as being production ready. The specification itself is under active development and developers are encouraged to review it only with the objective of familiarizing themselves with an up and coming standard. Do not use it in production sites yet!

Setting your development environment up

Before we proceed with the API itself, you might want to spend some time setting up your development environment up. For Internet Explorer (IE), an experimental implementation of the specification is available for download from Microsoft’s HTML5 Labs site. “HTML5 Labs” is an initiative from Microsoft for publishing prototype implementations of various key HTML5 specifications so that developers are able to experiment with them and provide feedback. The prototype implementation for Indexed DB works on Internet Explorer versions 8 and up and installing it is fairly straightforward.

  1. Download the prototype by clicking on the link “Download the Protoype now!” from here.

  2. Unzip the downloaded file.

  3. If you are running a 32-bit version of Windows then run vcredist_x86.exe.

  4. Register “sqlcejse40.dll” by running the following command from an elevated command prompt:

    regsvr32 sqlcejse40.dll

    If everything goes well, then you should see a screen such as the following:

     reg

Alternatively, get a recent version of Google Chrome or Firefox and you should be all set.

Coming up…

Now that we have the fundamentals covered, we’ll go ahead and get started with the API itself from the next post. Stay tuned!

Link Comment
 
On JavaScript code blocks, variable scope and variable hoisting
Technobabble
9/2/2011 10:50:07 PM  

I am currently reading the book JavaScript Patterns, by Stoyan Stefanov and I came across this interesting tidbit of information regarding variable scopes in JavaScript.  Consider the following C++ code:

#include <iostream>

int main() {
  if(true) {
    int foo = 10;
  }

  std::cout<<foo;
  return 0;
}

Compiling this produces the following error:

scope.cpp(8) : error C2065: 'foo' : undeclared identifier

This is only to be expected. In C++, code blocks automatically define a variable scope, i.e., variables defined inside the code block are not visible outside of it. The same behavior can be observed in many of the other popular programming languages in use today such as C# and Java with one notable exception – JavaScript! Smile Consider the following JavaScript snippet:

function scope() {
    if(true) {
        var foo = 10;
    }
    print(foo);
}

scope();

Interestingly, this prints the value “10”! What’s going on here? The first thing one needs to understand about JavaScript is the notion of “variable hoisting”. Regardless of where you have declared a variable, the runtime treats it as though it has been declared at the start of the scope. Here’s an example:

function hoist() {
    try {
        print(undeclaredVariable === undefined);
        print("Yep, was able to refer to 'undeclaredVariable'.");
    }
    catch(e) {
        print("Could not use 'undeclaredVariable'.");
    }
    
    try {
        print(declaredLater === undefined);
        print("Yep, was able to refer to 'declaredLater'.");
    }
    catch(e) {
        print("Could not use 'declaredLater'.");
    }
    
    var declaredLater = 10;
}

hoist();

Running this snippet produces the following output:

Could not use 'undeclaredVariable'.
true
Yep, was able to refer to 'declaredLater'.

In the function “hoist” we have declared a variable named “declaredLater” right at the end of the function body.  Interestingly, while we were unable to refer to an undeclared variable (evidenced by the fact that the “print” statement inside the first “catch” block got executed) we seem to have no problems referring to “declaredLater” even before it is actually declared. This is so because the JavaScript runtime “hoists” all the local variables declared in the function to the top of the function.  It is as though the function had been defined like so:

function hoist() {
    var declaredLater;

    // stuff here
    
    declaredLater = 10;
}

hoist();

Note that though the variable itself gets declared at the top of the function, the initialization still happens only on the line where it occurred in the original function - which explains why the statement print(undeclaredVariable === undefined) ends up printing true.

Now, why did our “scope” function work the way it did? Turns out that in JavaScript curly braces do not introduce a variable scope. Only functions do. This means that variables declared inside of “if”, “for”, “while” etc. behave as though they have been declared at the top of the function. The aforementioned book therefore recommends moving all variable declarations to the top of the function. So, instead of doing the following:

function foo() {
    var a = [1, 2, 3, 4];
    if(someCondition() === true) {
        var b, c, d;
        // do something with b, c, d
    }
    for(var i = 0; i < a.length ; ++i) {
        // do something with "a"
    }
}

Do something like the following:

function foo() {
    var a = [1, 2, 3, 4], b, c, d, i, len = a.length;

    if(someCondition() === true) {
        // do something with b, c, d
    }
    for(i = 0; i < len ; ++i) {
        // do something with "a"
    }
}

This makes it clear that the scope of the variables in question is the function “foo”.

The key takeaways therefore are the following:

  • Code blocks do not determine variable scope in JavaScript. Functions are the only construct that can be used to limit scope of variables.
  • Regardless of where variables are declared, the runtime always treats them as though they have been declared at the beginning of the function in question.
  • If you need some global code to be executed, then use an immediate function (also known as a “self-executing” function) to limit the scope of the local variables you use in that code. So instead of doing the following:

    <script>
      var v1, v2;
      // some code here that uses v1 and v2
    </script>

    Use code like this:

    <script>
      (function() {
          var v1, v2;
          // some code here that uses v1 and v2
      })();
    </script>
Link Comment (7)
 
JavaScript strict mode restrictions
Technobabble
9/1/2011 11:04:52 PM  

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.

Link Comment
 
On JavaScript strict mode
Technobabble
9/1/2011 2:50:25 AM  

ECMAScript version 5 is the latest revision of the ECMAScript programming language ratified for widespread adoption by Ecma International – the standards body responsible for figuring out what goes into the language. If you’re wondering what exactly ECMAScript is and why you should be interested, it turns out that ECMAScript is standardese for a language that the rest of the world knows as JavaScript. Strictly speaking (no pun on title intended) JavaScript and ECMAScript aren’t identical. JavaScript is a dialect of ECMAScript but the differences are mostly negligible and are largely there for historical backward compatibility reasons. ECMAScript 5 (henceforth referred to as ES5) brings a suite of interesting features to the table, many of which have as their goal, the introduction of greater programming discipline to the language.

In this post (and over the next few posts), we review one such feature which directly addresses some of the more notorious parts of the language. This new feature is known as “strict mode”, which briefly, is a whole new execution mode for JavaScript that causes the execution engine to run code with slightly different semantics.

What is strict mode?

“Strict mode” is a way of causing the runtime engine to interpret and execute JavaScript with different semantics than what one sees with unrestricted code. Code running in strict mode has the following characteristics:

  1. Excludes some syntactic and semantic features, i.e., you can’t do some things that you are allowed to do otherwise
  2. Modifies semantics of some features, i.e., same code runs differently in strict mode compared to what happens in unrestricted mode
  3. An expanded list of scenarios that cause errors to be raised instead of them being silently ignored or running with assumed intent
  4. Applies to specific code units – i.e., you cannot have strict mode apply to all your .js files at one shot (unless you concatenate all of them of course, in which case its one code unit as far as the runtime is concerned)

The basic rationale behind strict mode is the introduction of some runtime enforced discipline to JavaScript development. I have always felt that JavaScript is far too dynamic for its own good and in my opinion, strict mode tries to address some of that excessive dynamism. Many of the tricky parts of the language that required programmer discipline are now enforced by the engine when you mark a particular code segment as “strict”. Can you spot the bug in the following snippet? With “strict mode” turned on, the runtime will!

function findProduct(numbers) {
    var product = 0,
        len = numbers.length;
    for(var i = 0; i < len; ++i) {
        prodct = product * numbers[i];
    }
    return product;
}

Browser support

Pretty much all modern browsers support strict mode in their respective JavaScript engines. On Internet Explorer (IE) strict mode is available from version 10 onwards. You can download the latest platform preview of IE10 from the IE test drive site. All the samples in this article have been tested on IE10 platform preview 2 (IE10 PP2) using a JavaScript eval console I had put together sometime back. Selected samples have been tested on Google Chrome 14 and Firefox 7 beta as well.

Strict mode contexts

Running a piece of JavaScript under strict mode is simplicity itself. Here’s an example:

"use strict";
alert("Look ma! Strict mode!");

The nice thing here is that this is perfectly valid ECMAScript 3 code as well (ES3 is the previous edition of ECMAScript. What happened to ES4? It went the way of the dodo!). An ES3 JavaScript engine will simply ignore the noop line and proceed with running the rest of the script. In fact, this sort of syntactic backward compatibility with ES3 has been a key design goal for ES5 and a surprisingly large part of the ES5 specification can be implemented completely in ES3 JavaScript. Strict mode however, is an example of an ES5 feature that perhaps cannot be implemented purely in ES3 JavaScript without additional support from the runtime.

The following kinds of JavaScript code can be made to run under strict mode:

  1. Global code

    This is basically executable code that you enter inside a script tag. For example:

    <script>
      "use strict";
      // global strict mode code here
    </script>

    Note that with HTML5, there is no longer a need to add the type attribute for script tags.

  2. Eval code

    Eval code that has the strict mode directive prefix:

    eval("'use strict'; // strict code here");

    Or is invoked from strict mode code:

    "use strict";
    eval("// strict code here");
  3. Function code

    Functions that have the strict mode directive prefixed before the rest of the code (putting the directive anywhere else doesn’t count):

    function foo() {
        "use strict";
        // strict code here
    }

    Functions declared in strict mode code inherit the strictness:

    function foo() {
        "use strict";
        var bar = function () {
            // strict code here
        };
        bar();
    }

    Note that the latter case is particularly relevant when you are defining callbacks for various event handlers. Also, note that strictness does not extend across call stacks. Here’s an example:

    function foo() {
        // not strict mode even though
        // foo is being invoked from a
        // "strict" function
    }
    
    function bar() {
        "use strict";
        foo();
    }
    
    bar();

In the next post we’ll start taking a look at some of the restrictions that ES5 strict mode imposes on the language and how they help you write better JavaScript.

Link Comment (1)
 
blogorama home
about this blog
email the author
where on earth am i?
subscribe to mailing list
feeds Use these links for feed syndication
rss  |  atom
by category
technobabble (60)
philosophical crud (3)
irrelevant stuff (7)
archive
november, 2011 (2)
october, 2011 (1)
september, 2011 (7)
july, 2011 (3)
june, 2011 (2)
may, 2011 (3)
april, 2011 (1)
march, 2011 (1)
february, 2011 (1)
february, 2010 (1)
october, 2009 (1)
september, 2009 (1)
july, 2009 (5)
march, 2009 (2)
august, 2008 (2)
march, 2008 (1)
january, 2008 (1)
september, 2007 (2)
april, 2007 (1)
february, 2007 (2)
december, 2006 (1)
october, 2006 (1)
september, 2006 (4)
august, 2006 (3)
july, 2006 (4)
june, 2006 (3)
may, 2006 (6)
april, 2006 (2)
recent entries
Implementing variab...
Debugging existing...
Screen scraping wit...
Building an Instagr...
Building an Instagr...
Organizing your Jav...
298021 hits