Thinking About ECMAScript 5 Parts

The ECMAScript Language Specification Edition 5.1 (i.e. ES5 or ECMAScript 5) was finalized in June 2011 (drafted 2009) and the first browser to fully support the specification was Firefox 4, released on April 20th 2011.

2014 is around the corner and my in-depth ES5 implementation knowledge remains rather rough. The reality is, I have not yet had the luxury of working on a project full-time that did not require ie8 support, which, as you might know, does not run an ES5 scripting engine. Before you cry shim, I've personally chosen to avoid a comprehensive shim for ES5 given the caveats. The caveats in this instance scare the pee out of me.

ES3 has been my available tool and it has had years of use. However, coming soon is a day when ie8 usage will drop and supporting it will no longer be required. On that day, a new set of JavaScript native parts will become available without shims. Are you ready for that day?

Before I begin, I want to clarify that this article will focus on ES5 parts excluding strict mode details. This means the parts discussed here assume that strict mode is not in use. Strict mode (i.e. 'use strict';) is a large topic and I will save this topic for another article.

With that said, let's start looking at some ES5 parts worth thinking about.

Modern Browsers Support ES5 With The Following Exceptions

In general, modern browsers (ie9+, chrome 19+, safari 5+, firefox 4+, opera 12+, Android 3+, Blackberry Browser 7+, iOS safari 5+, opera mobile 12+, ie Mobile 10+) conform to ECMAScript 5.1 except that ie9 does not support Strict Mode and Safari 5.1+ does not support zero-width chars in identifiers (e.g. var $\u200c = 'a', or {_\u200d: 'vuur'}. Note that it is not until ie10 that 'use strict'; is supported.

ie8 Is A ES3 Scripting Engine

Like I have already stated, ie8 does not conform to ECMAScript 5.1 and a few exceptions exist like JSON, Object.defineProperty, and Object.getOwnPropertyDescriptor. However, it is best to think of IE8 as an ES3 Browser.

Remembering this becomes critical when selecting third party solutions. Many solutions have chosen not to support ie8, regardless of its widespread use, in order to write native ES5 code without shimming.

from netmarketshare.com

I believe it goes without saying, but if you need to support ie8 make sure any of the third party solutions you use also support it. If the third-party solution makes use of ES5 updates but claims support for ie8 then you will next have to determine if you are ok with how the solution has been shimmed (e.g. global shim, privately scoped shim, comprehensive shim v.s. shimming only what is used etc..).

Official Trailing Commas

ES5 clarifies that having a comma following the last property of an object should not cause an error. Can I get an "Amen"?!?

Additionally ES5 expressly allows a trailing comma when defining array elements and it does not cause an undefined entry at the end of the array which occurred in some older ES3 browsers.

Multiline String Literals Using \

To create a multiline string literal, you can now simply end a line with the \ escape character.

//no error in ES5 scripting engines
var stringLiteral = "Lorem ipsum dolor sit amet, consectetur \ 
elit, sed do eiusmod tempor incididunt ut labore et dolore \
magna aliqua. Ut enim ad minim veniam, quis nostrud \
exercitation ullamco laboris nisi ut aliquip ex ea";

It is not great (i.e. you can't use indenting at the start of a new line and an empty space after \ will cause an error), but some think it better than what ES3 offered. It potentially get's better in ES6.

No Reserve Word Restrictions On Object Property Names

An error will not be thrown when using reserved keywords as property names (i.e. myObject.function will not throw an error). Use at your own risk.

ES5 Defines JSON Methods

ES5 scripting engines officially (i.e. it is officially in the specification) have native methods (i.e JSON.parse and JSON.stringify) for parsing and stringify'ing JSON. Note that some browser manufacturers provided these methods without implementing the full ES5 spec. (e.g. ie8).

enter image description here

Finally A Native 'string'.trim()!

The ES5 specification defines a trim() method for trimming whitespace from both ends of a string.

return '   foo   '.trim(); //returns 'foo'

Note that the trimRight() and trimLeft() while found in many modern browsers (e.g. Chrome, Safari, Opera, Firefox) remain non-standard methods.

Array-like Character Access On Strings

ES3 did not permit accessing individual characters of a primitive string using bracket notation (i.e. operate like string is array-like). This was changed in ES5 and access to individual characters in a string is permitted using bracket notation.

return 'ES5'[1]; // returns "S"

undefined, NaN, and Infinty Are Read Only Constants

Prior to ES5 the undefined, NaN, and Infinty global properties were read and write. Which means that one could change these global properties (e.g. undefined = 'foo';). In ES5 they are read-only properties. No longer does one have to wonder when using third party code or working with a large team of developers if undefined is really undefined. These properties can now be considered constants.

Will A Real Array Please Stand Up

In ES3 using instanceOf (e.g. [] instanceOf Array) to determine if a value is an array is not totally bullet proof. So isArray() was added. Use it, it's as close to totally bullet proof as it gets.

return Array.isArray([]); //returns true

New Array Methods

The ES5 specification officially defined the following 8 new Array methods.

The addition of these methods might not exactly be news to some of us. However, you should keep in mind that the specification also considers these methods generic. Meaning you can use them on strings and array-like objects.

Setting this For Callback Sent To forEach(), every(), some(), filter(), map() Array Methods

The forEach(), every(), some(), filter(), and map() array methods all accept an optional third argument which sets the this value for the callback function passed to the method. In the code below I set up the arrayLikeObject object, which I pass to the forEach method as the this value for the callback. Inside of the callback function this.length will get me the length property found in arrayLikeObject.

New Accessor Object Properties

In ES3 an object had one kind of property, a data property, which was simply a named value. ES5 added accessor properties, which is a property that can invoke a function when it is read (i.e. get) or written (i.e. set).

In the code below I create a name accessor property giving it both a get and set function.

Remember, you cannot have a data property with the same name as an accessor property. Doing so will throw an error.

New Configurable Object Property Attributes

An object property has attributes, if you weren't aware, and in ES3 these attributes were for internal use only. ES5 added new attributes and made some of these attributes readable and writable parts of the language. Think of property attributes as an object of attributes for each object property. Below I show the default attributes for both data and accessor properties using the new Object.getOwnPropertyDescriptor() function which returns an object representing a properties attributes.

Remember, data properties defined using dot notation or bracket notation are enumerable, configurable, and writeable (i.e. writable: true, enumerable: true, configurable: true) by default. And, accessor properties defined using dot notation or bracket notation are enumerable and configurable (i.e. enumerable: true, configurable: true) by default.

New Syntax For Defining & Modifying An Object's Properties

Given the new property type (i.e. accessors) and access to new property attributes ES5 offers the Object.defineProperty() and Object.defineProperties() functions for creating object properties and their attributes.

The Object.defineProperty() function is used to define a single property and its attributes. In the code below I use Object.defineProperty() to create the age and name properties.

var data = {}; //create data object
Object.defineProperty( //define properties on data object
    data,
    "age", 
    {
        value: 38,
        writable: true,
        enumerable: true,
        configurable: true
    }
);

var accessor = {}; //create accessor object
Object.defineProperty( //define properties on accessor object
    accessor,
    "name", 
    {
        get: function () {},
        set: function (value){},
        enumerable: true,
        configurable: true
    }
);

//logs {value: 38, writable: true, enumerable: true, configurable: true} 
console.log(Object.getOwnPropertyDescriptor(data,'age'));

//logs {get: function, set: function, enumerable: true, configurable: true}
console.log(Object.getOwnPropertyDescriptor(accessor,'name'));

The Object.defineProperties() function is used to define a multiple properties and their attributes. In the code below I use Object.defineProperties() to create the age and name properties.

var cody = {}; //create cody object
Object.defineProperties( //define age and name properties on cody object
    cody, 
    {
        "age": {
            value: 38,
            writable: true,
            enumerable: true,
            configurable: true
        },
        "name": {
            get: function(){},
            set:function(value){},
            enumerable: true,
            configurable: true
        }
    }
);

//logs {value: 38, writable: true, enumerable: true, configurable: true} 
console.log(Object.getOwnPropertyDescriptor(cody,'age'));

//logs {get: function, set: function, enumerable: true, configurable: true}
console.log(Object.getOwnPropertyDescriptor(cody,'name'));

Remember, properties defined using Object.defineProperty() and Object.defineProperties() are NOT enumerable, configurable, and writeable (i.e. writable: true, enumerable: true, configurable: true) by default. And, accessor properties defined using Object.defineProperty() and Object.defineProperties() are NOT enumerable and configurable (i.e. enumerable: true, configurable: true) by default.

Listing An Objects Properties Using Object.keys() and Object.getOwnPropertyNames()

Two new functions in ES5 will extract an objects properties, not including inherited properties, and return these properties in an array. The Object.key() function will return an array of all properties that are enumerable and the Object.getOwnPropertyNames() will return an array of all properties, including non-enumerable properties.

Levels (i.e. extending, sealing, freezing) Of Protecting An Objects Properties

ES5 objects can be set to be non-extensible, sealed, or frozen. These settings are intended to be levels of property protection which can be switched on by using the Object.preventExtensions(myObject), Object.seal(myObject), and Object.freeze(myObject) functions.

Preventing extension using Object.preventExtensions() entails:

Sealing using Object.seal() does everything Object.preventExtensions() will do including:

Freezing using Object.freeze() does everything Object.preventExtensions() and Object.seal() will do including:

Here is a table to help remember the above details.

preventExtensions() seal() freeze()
Prevents new properties? Yes Yes Yes
Prevents configuration of existing properties? No Yes Yes
Prevents assigning existing data properties? No No Yes

Additionally ES5 offer's 3 corresponding functions for determine (i.e. returns a boolean value) the level of protection on an object:

Explicitly Set Inheritance Using Object.create()

ES3 permitted the creation of Object object's in the following ways which implicitly arranges inheritance (i.e. prototypal inheritance via prototype chain).

var objectViaObjectLiteral = {};
var objectViaObjectConstructor = new Object();
var objectViaCustomConstructor = new(function Person(){});

ES5 has added another way to create an Object object using the Object.create() function which allows the developer to explicitly setup what object, the object being created inherits from. Using Object.create() one can replicated the inheritance that is setup when creating an object literal or object instance from the Object constructor.

var object = Object.create(Object.prototype); //same as {} or new Object()

The purpose of Object.create() lies in the fact that you can create an object and specify which object is inherited. However if you don't pass Object.create() any arguments the object created has its prototype set to Object.prototype (yes, the above code example is what occurs by default). If null is passed as the first argument then the object being created will not inherit from anything (yes, not even from Object.prototype). A simple example of using Object.create() is shown below.

Notice in the above example that the Object.create() method also takes a second argument which is basically the same value that is sent to Object.defineProperties().

Getting An Objects Prototype Using getPrototypeOf()

The non-standard __proto__ (i.e. 'foo'.__proto__ === String.prototype) is a reference to an objects prototype. The Object.getPrototypeOf() function, which returns a reference to the prototype of the object you pass to it, is the standard way of getting an object's prototype in ES5.

In the code example below I show that passing Object.getPrototypeOf() a string object returns the same reference found at String.prototype.

Object.getPrototypeOf(new Object('foo')) === String.prototype //return true

Creating Functions With A Custom this Using New bind() Method

In ES3 we could immediately invoke a function setting the this reference during invocation using call() or apply(). ES5 adds the bind() method, which instead of invoking a function creates a new function which sets the this referenced from the scope of the function to the object argument passed in.

In the code below I call the bind() method on myFunction, creating a new function, that when invoked will map it's this (i.e. reference) to myObject.

Conclusion

We've touch on the major parts of ES5 in this article but technically not all of them. For a comprehensive overview I'd like to suggest, "Crockford on JavaScript - Level 7: ECMAScript 5: The New Parts".

Additionally, ES5 details in context with the language as a hole can be easily extracted from the soon to be released book, "The programmer’s guide to JavaScript" by Dr. Axel Rauschmayer, which will offer a free to read online version upon release.