Level 7 ECMAScript 5: The New Parts
Any change to a standard is an act of violence.
Complete implementations of ECMAScript, Fifth Edition, are now beginning to appear in the best web browsers.
ECMAScript 1999 Third Edition 2009 Fifth Edition Default Strict ES3 ES5 For the short term, work in the intersection of ES3 and ES5/Strict. For the long term, work with ES5/Strict. Avoid ES5/Default.
A better JavaScript.
Lots of Little Things Make the standard conform better to reality. Make the browsers conform better to each other. Where the browsers disagreed, we took license to correct the standard. Interoperability will be improved. If you program well, this should have little impact on you.
Goals of ES5 Don't break the web. Improve the language for the users of the language. Third party security (mashups). Protect stupid people from themselves. No new syntax.
New Syntax Causes syntax errors on IE < 9.
Trailing Commas { "trailing": "comma", [ ] "trailing", "comma",
Reserved Word Relaxation No reserved word restrictions on property names. var a = { return: true, function: liberty, class: 'classitis', ; a.return = false; a.function(); alert(a.class);
Getters and Setters function make_temperature(temperature) { return { get celsius() { return temperature;, set celsius(value) { temperature = value;, get fahrenheit() { return temperature * 9 / 5 + 32;, set fahrenheit(value) { temperature = (value - 32) * 5 / 9; ;
Multiline string literals var long_line_1 = "This is a \ long line"; // ok var long_line_2 = "This is a \ long line"; // syntax error
Constants Infinity NaN undefined
parseint parseint('08') === 8
Regexp Literals /regexp/ now produces new regular expression objects every time.
Replacing Object or Array does not change the behavior of { or []. As if by the original
Brand New Methods Methods can be added without breaking syntax.
JSON JSON.parse(text, reviver) JSON.stringify(value, replacer, space) json2.js https://github.com/douglascrockford/json-js
Function.prototype.bind if (!Function.prototype.hasOwnProperty('bind')) { Function.prototype.bind = function (object) { var slice = Array.prototype.slice, func = this, args = slice.call(arguments, 1); return function () { return func.apply(object, args.concat( slice.call(arguments, 0))); ; ;
String.prototype.trim if (!String.prototype.hasOwnProperty('trim')) { String.prototype.trim = (function (re) { return function () { return this.replace(re, "$1"); ; (/^\s*(\s*(\s+\s+)*)\s*$/));
Array.prototype.every if (!Array.prototype.hasOwnProperty('every')) { Array.prototype.every = function(fun, thisp) { var i, length = this.length; for (i = 0; i < length; i += 1) { if (this.hasownproperty(i) &&!fun.call(thisp, this[i], i, this)) { return false; return true; ;
Array.prototype.filter if (!Array.prototype.hasOwnProperty('filter')) { Array.prototype.filter = function(fun, thisp) { var i, length = this.length, result = [], value; for (i = 0; i < length; i += 1) { if (this.hasownproperty(i)) { value = this[i]; if (fun.call(thisp, value, i, this)) { result.push(value); return result; ;
Array.prototype.forEach if (!Array.prototype.hasOwnProperty('forEach')) { Array.prototype.forEach = function (fun, thisp) { var i, length = this.length; for (i = 0; i < length; i += 1) { if (this.hasownproperty(i)) { fun.call(thisp, this[i], i, this); ;
Array.prototype.indexOf if (!Array.prototype.hasOwnProperty('indexOf')) { Array.prototype.indexOf = function (searchelement, fromindex) { var i = fromindex 0, length = this.length; while (i < length) { if (this.hasownproperty(i) && this[i] === searchelement) { return i; i += 1; return -1; ;
Array.prototype.lastIndexOf if (!Array.prototype.hasOwnProperty('lastIndexOf')) { Array.prototype.lastIndexOf = function (searchelement, fromindex) { var i = fromindex; if (typeof i!== 'number') { i = this.length - 1; while (i >= 0) { if (this.hasownproperty(i) && this[i] === searchelement) { return i; i -= 1; return -1; ;
Array.prototype.map if (!Array.prototype.hasOwnProperty('map')) { Array.prototype.map = function(fun, thisp) { var i, length = this.length, result = []; for (i = 0; i < length; i += 1) { if (this.hasownproperty(i)) { result[i] = fun.call(thisp, this[i], i, this); return result; ;
Array.prototype.reduce if (!Array.prototype.hasOwnProperty('reduce')) { Array.prototype.reduce = function (fun, initialvalue) { var i, length = this.length; for (i = 0; i < length; i += 1) { if (this.hasownproperty(i)) { initialvalue = fun.call(undefined, initialvalue, this[i], i, this); return initialvalue; ;
Array.prototype.reduceRight if (!Array.prototype.hasOwnProperty('reduceRight')) { Array.prototype.reduceRight = function (fun, initialvalue) { var i = this.length - 1; while (i >= 0) { if (this.hasownproperty(i)) { initialvalue = fun.call(undefined, initialvalue, this[i], I, this); i -= 1; return initialvalue; ;
Array.prototype.some if (!Array.prototype.hasOwnProperty('some')) { Array.prototype.some = function(fun, thisp) { var i, length = this.length; for (i = 0; i < length; i += 1) { if (this.hasownproperty(i) && fun.call(thisp, this[i], i, this)) { return true; return false; ;
Date.now() if (!Date.hasOwnProperty('now')) { Date.now = function () { return (new Date()).getTime(); ;
Date.prototype.toISOString if (!Date.prototype.hasOwnProperty('toISOString')) { Date.prototype.toISOString = function () { function f(n) { return n < 10? '0' + n : n; ; return this.getutcfullyear() + '-' + f(this.getutcmonth() + 1) + '-' + f(this.getutcdate()) + 'T' + f(this.getutchours()) + ':' + f(this.getutcminutes()) + ':' + f(this.getutcseconds()) + 'Z';
Date new Date(string) and Date.parse(string) will try ISO format first.
Array.isArray if (!Array.hasOwnProperty('isArray')) { Array.isArray = function (value) { return Object.prototype.toString.apply(value) === '[object Array]'; ;
Object.keys if (!Object.hasOwnProperty('keys')) { Object.keys = function (object) { var name, result = []; for (name in object) { if (Object.prototype.hasOwnProperty.call(object, name)) { result.push(name); return result; ;
Object.create if (!Object.hasOwnProperty('create')) { Object.create = function (object, properties) { var result; function F() { F.prototype = object; result = new F(); if (properties!== undefined) { Object.defineOwnProperties(object, properties); return result; ;
Meta Object API Control over the attributes of the properties of the objects.
Two kinds of properties: Data properties Accessor properties
Attributes A property is a named collection of attributes. value: any JavaScript value writeable: boolean enumerable: boolean configurable: boolean get: function () { return value; set: function (value) {
Data Property var my_object = {foo: bar; var my_object = Object.create(Object.prototype); Object.defineProperty(my_object, 'foo', { value: bar, writeable: true, enumerable: true, configurable: true );
Accessor property Object.defineProperty(my_object, 'inch', { get: function () { return this.mm / 25.4;, set: function (value) { this.mm = value * 25.4;, enumerable: true );
Meta Object API Object.defineProperty(object, key, descriptor) Object.defineProperties(object, object_of_descriptors) Object.getOwnPropertyDescriptor( object, key)
Object.getOwnPropertyNames(object) Object.getPrototypeOf(object) Best not to use these. Secure frameworks may ban their use.
function replace_prototype(object, prototype) { var result = Object.create(prototype); Object.getOwnPropertyNames(object).forEach(function (key) { Object.defineProperty(result, key, Object.getOwnPropertyDescriptor( object, key)); ); return result;
Object Extensibility Object.preventExtentions(object) Object.seal(object) Object.freeze(object) Object.isExtensible(object) Object.isSealed(object) Object.isFrozen(object)
Unintended Inheritance var word_count = {; text.split(/[\s.,?!":;]+/).foreach(function count_word(word) { if (word_count[word]) { word_count[word] += 1; else { word_count[word] = 1; ); Accidental collisions: Fails when word === 'constructor'
Solutions in ES5 Object.create(null) creates an object that does not inherit anything. Set the enumerable attribute to false when adding methods to prototypes. That keeps them out of for in enumerations. Object.keys(object) produces an array of strings, the enumerable keys of the own (not inherited) properties.
Strict Mode The most important new feature in ECMAScript, Fifth Edition.
Strict Mode Backward compatible pragma. 'use strict'; File form. First statement in a file. (avoid) Function form. First statement in a function.
implements interface let package private protected public static yield New Reserved Words
Strict Mode No more implied global variables within functions. this is not bound to the global object by function form. apply and call do not default to the global object. No with statement. Setting a writeable: false property will throw. Deleting a configurable: false property will throw. Restrictions on eval. eval and arguments are reserved. arguments not linked to parameters. No more arguments.caller or arguments.callee. No more octal literals. Duplicate names in an object literal or function parameters are a syntax error.
Forgetting to use the new prefix will now throw an exception, not silently clobber the global object. Death Before Confusion!
Strict Mode addeventlistener( ); //error window.addeventlistener( ); //ok
Strict Mode There are no methods for determining if strict mode is on, but it is easy to make your own. function in_strict_mode() { return (function () { return!this; ()); function strict_mode_implemented() { return (function () { 'use strict'; return!this; ());
MASH
Safe JavaScript Subsets The design of Strict Mode was informed by safe subsets like Caja & ADsafe. Caja. ADsafe. http://code.google.com/p/google-caja/ http://www.adsafe.org/
IE6 MUST DIE!
IE7 MUST DIE!
IE8 MUST DIE!
IE9 MUST DIE!
IE10
What next? Nothing is certain yet. Lighter, more expressive syntax. More expressive literals. Tail recursion. Modules. Proxies. Fixed typeof operator. Much more.
Next time: Section 8