Introducing ECMAScript 5.1
Table of contents:
- Introduction
- Browser support
- The Strict Mode of ES5
- JSON
- Object Additions
- Array Extras
- Function.prototype.bind
- Additional References
Introduction
ECMAScript 5.1 (or just ES5) is the latest revision of the ECMAScript standard — the specification that JavaScript is based on. Similar in spirit to the HTML5 specification process, ES5 standardizes existing JavaScript implementations in conjunction with additions to the language and native ECMAScript objects. ES5 also introduces a strict variant of the language known as "strict mode".
In this article we'll introduce some of the most useful changes and additions. For a full list, consult Appendices D and E of the official ECMAScript language specification (PDF download, 3MB), available from http://www.ecmascript.org/; you can also see this content in HTML form, at Michael[tm] Smith's unofficial annotated HTML version.
Browser Support
With the release of Opera 11.60, all of the five major browsers now support ES5, save for implementation bugs. Unless otherwise stated, everything mentioned in this article can be used in the following browser versions (or higher):
- Opera 11.60
- Internet Explorer 9*
- Firefox 4
- Safari 5.1**
- Chrome 13
* IE9 does not include support for strict mode — this is added in IE10.
** Safari 5.1 still lacks support for Function.prototype.bind
, although Function.prototype.bind
support has recently been added to Webkit.
For information on support in older browsers, see Juriy Zaytsev's excellent ECMAScript 5 compatibility table.
The Strict Mode of ES5
Strict mode is a way for authors to opt-in a more restrictive variant of the language — providing additional reliability for authors and safety for users. Enabling strict mode is done by adding the "use strict" directive at the top of a JavaScript file or function. Since the "use strict" directive is just a string, it will be safely ignored by older browsers.
"use strict";
function strict(){
"use strict";
//...
}
function sloppy(){
eval("window.foo = 'bar'");
}
Many things that cause surprising or buggy behavior at runtime in a script will throw errors in strict mode, for example:
- Assignment to an undeclared variable throws a
ReferenceError
, rather than creating a global variable. - Assigning an identical property more than once in an object literal throws a
SyntaxError
. - Use of the
with
statement throws aSyntaxError
.
MDSN's strict mode article has a useful table summarising all of these differences.
JSON
ES5 specifies a global JSON
object for serializing (JSON.stringify
) and deserializing (JSON.parse
) objects into the JSON format.
For older browsers, consider using Douglas Crockford's json2.js, which implements the same functionality in older browsers (after feature-testing for native support).
JSON.parse(text [, reviver])
JSON.parse
takes text (formatted as JSON) and returns an ECMAScript value. The optional reviver argument is a function with two arguments, key
and value
, that operates on the results — making it possible to filter or transform what gets returned.
>> var result = JSON.parse('{"a": 1, "b": "2"}');
Object
>> result.b
"2"
If we want to ensure that the value is an integer upon parsing, we can use the reviver function for that.
var result = JSON.parse('{"a": 1, "b": "2"}', function(key, value){
if (typeof value == 'string'){
return parseInt(value);
} else {
return value;
}
})
>> result.b
2
JSON.stringify(value [, replacer [, space]])
JSON.stringify
allows authors to take an ECMAScript value and convert it to a JSON-formatted string. In its simplest form, JSON.stringify
takes a value and returns a string.
>>> var mike = JSON.stringify({mike: "taylor"})
undefined
>> mike
'{"mike": "taylor"}'
>> typeof mike
"string"
If we need to to alter the way that the value is stringified, or provide a filter for what gets selected, we can pass it a replacer function. For example, if we want to filter out properties with the value of 13 from our object to be stringified,
var nums = {
"first": 7,
"second": 14,
"third": 13
}
var luckyNums = JSON.stringify(nums, function(key, value){
if (value == 13) {
return undefined;
} else {
return value;
}
});
>> luckyNums
'{"first": 7, "second": 14}'
If the replacer function returns undefined
, the key/value pair will not be included in the resulting JSON. We can also pass in a space argument to aid in the readability of what gets returned. Possible values can be a number indicating the number of spaces to indent at each level of the JSON string or a string to serve as the indentation. A number over 10, or a string with more than 10 characters will result in a spacer that is truncated to 10 or the first 10 characters.
var luckyNums = JSON.stringify(nums, function(key, value) {
if (value == 13) {
return undefined;
} else {
return value;
}
}, 2);
>> luckyNums
'{
"first":7,
"second":14
}'
Object Additions
The following methods are added to the Object
constructor:
Object.getPrototypeOf
Object.getOwnPropertyDescriptor
Object.getOwnPropertyNames
Object.create
Object.defineProperty
Object.defineProperties
Object.seal
Object.freeze
Object.preventExtensions
Object.isSealed
Object.isFrozen
Object.isExtensible
Object.keys
One of the benefits of these additions is more control over an object's properties, e.g., what is allowed to be modified, enumerated over, deleted, etc. This is done via programmatic access to an object's property descriptors. For example:
var cat = {};
Object.defineProperty(cat, "name", {
value: "Maru",
writable: false,
enumerable: true,
configurable: false
});
Object.defineProperty(cat, "skill", {
value: "exploring boxes",
writable: true,
enumerable: true,
configurable: true
});
For our cat
object, its name
cannot be changed, but will appear in a for-in
loop. Among other things, Maru is good at exploring boxes, but that could change in the future so the skill
property is left writable
and configurable
.
In a future article we'll explore all of the Object additions in more detail.
Array Extras
The following methods are additions to the Array prototype
object:
Array.prototype.indexOf
Array.prototype.lastIndexOf
Array.prototype.every
Array.prototype.some
Array.prototype.forEach
Array.prototype.map
Array.prototype.filter
Array.prototype.reduce
Array.prototype.reduceRight
Dmitry Soshnikov has written an in-depth reference article on the ES5 array "extras".
One thing not mentioned in Dmitry's article is Array.isArray
, which as you can see sits directly on the Array
constructor — not the prototype
object.
Array.isArray
does pretty much what you would expect it to do — it's a method that returns true
or false
depending on if the argument's [[Class]] internal property is "Array".
Array.isArray("NO U")
>> false
Array.isArray(["NO", "U"])
>> true
In ES3, the only reliable way to determine if a value is an array was by using "the Miller Device", i.e., comparing the internal [[Class]]
property to that of an array.
Object.prototype.toString.apply(value) === '[object Array]'
Function.prototype.bind(thisArg [, arg1 [, arg2, …]])
Function.prototype.bind
returns a new function object with its this value bound to the thisArg
argument. Essentially, this allows you to execute a function within the scope of some other object.
function locate(){
console.log(this.location);
}
function Maru(location){
this.location = location;
}
var kitty = new Maru("cardboard box");
var locateMaru = locate.bind(kitty);
locateMaru();
In this example, we can call the location
function within the context of the Maru object. As locate
is a property of the global object, its this
value is the global object (window
). In this case, we're looking for a cat, not a Location
object, so we can create a new method locateMaru
with the this
value bound to always be kitty
.
Additional References
- ECMAScript 5 Objects and Properties by John Resig
- Understanding JavaScript Function Invocation and “this” by Yehuda Katz
- JavaScript Strict Mode by Angus Croll
- ECMA-262-5 in detail: Introduction by Dmitry Soshnikov
- ECMAScript 5 compatibility table by Juriy Zaytsev
This article is licensed under a Creative Commons Attribution 3.0 Unported license.
Comments