ECMAScript 5:
The Definitive Slides
David Flanagan
http://davidflanagan.com
david@davidflanagan.com
Overview of ES5
- Useful new APIs
- Mostly things that can already be done in ES3
- Property and object attributes
- getters and setters, constants, non-enumerable methods, sealed and frozen objects
- partially implemented in latest Firefox and Chrome alphas.
- Strict semantics
- opt-in strict mode; not backward-compatible
- increases security and decreases bugs
- not yet implemented
Array Methods
-
forEach(),
map(),
reduce(),
filter(),
indexOf()
-
every(),
some(),
reduceRight(),
lastIndexOf()
- Most browsers implement these currently
- Easy to implement using ES3 for IE
Function.prototype.bind()
Binds a function to an object, and also does partial application.
Function.prototype.bind = function(o) {
// Save the this and arguments values
var self = this, boundArgs = arguments;
return function() {
// Build up an argument list
var args = [], i;
for(i = 1; i < boundArgs.length; i++)
args.push(boundArgs[i]);
for(i = 0; i < arguments.length; i++)
args.push(arguments[i]);
// Now invoke self as a method of o
return self.apply(o, args);
};
};
JSON
- JSON.parse()
- JSON.stringify()
- In ES3: <script src="json2.js"></script>
Object.keys()
- Returns an array of names of all enumerable own properties
- In ES3:
Object.keys = Object.keys || function(o) {
var result = [];
for(var name in o) {
if (o.hasOwnProperty(name))
result.push(name);
}
return result;
};
Object.keys().forEach() instead of for/in loop
Object.getOwnPropertyNames()
- Like Object.keys() but also returns names of non-enumerable properties
- No way to do this in ES3.
Other New APIs
- String.prototype.trim(): to strip leading and trailing whitespace
- Date.prototype.toISOString():
"2010-03-31T05:26:25.998Z"
- Date.parse() and new Date(string) try ISO format first
- Date.now() instead of (new Date()).getTime()
Getters and Setters
- ES5 standardizes the syntax supported by all but IE:
- ES5 does not standardize __defineGetter__() etc.
// Generate strictly increasing serial numbers
var serialnum = {
// Start at zero
$n: 0,
// Return a new value each time it is read
get next() { return this.$n++; },
// Set n, but only to a larger value
set next(n) {
if (n >= this.$n) this.$n = n;
else throw "Can't decrease n";
}
};
Property Attributes
- Properties aren't just name/value pairs.
- Data properties have have a name and:
- value
- writable attribute
- enumerable attribute
- configurable attribute
- Accessor properties have a name and:
- get attribute
- set attribute
- enumerable attribute
- configurable attribute
Attribute Meanings
- Enumerable props are visible to for/in loop
- Writable props can have their value changed.
- Configurable props can be deleted, and can have
their attributes altered.
- Non-configurable properties cannot be altered,
except to convert a writable property to a read-only property.
- Configurable but non-writable properties can be indirectly written
Property Descriptor
An object that describes (but does not name) a property:
var pi_descriptor = {
value: 3.14,
enumerable: true,
writable: false,
configurable: true
};
var timestamp_descriptor = {
enumerable: true, configurable: false,
get: function() {
(new Date()).toISOString();
},
set: undefined
};
Working with Descriptors
- Object.defineProperty(o, n, d)
- Object.defineProperties(o, {n1:d1,n2:d2,...})
- Creates or alters o.n1, o.n2...
- Object.create(proto, {n1:d1, n2:d2,...})
- Creates a new object with properties n1, n2...
- Object.getOwnPropertyDescriptor(o, n)
- Returns descriptor for o.n
Example 1
var serialnum = Object.create(Object.prototype, {
$n: {
value: 0,
writable: true,
enumerable:false,
configurable:false
},
next: {
enumerable:true,
configurable:true,
get: function() { return this.$n++; },
set: undefined
}
});
Example 2
function copyprops(f, t) {
var names = Object.getOwnPropertyNames(f);
names.forEach(function(n) {
if (n in t) return;
var d = Object.getOwnPropertyDescriptor(f,n);
Object.defineProperty(t, n, d)
});
}
Example 3
// Add an Object method that doesn't
// affect for/in loops
Object.defineProperty(
Object.prototype,
"extend",
{
enumerable: false,
writable: true,
configurable: true,
value: function(from) {
copyprops(from, this);
}
}
);
Example 4
// Define a class C with a superclass S.
// Give it a method m and a constant N.
function C() {}
C.prototype = Object.create(S.prototype, {
m: {
enumerable:false,
writable:true,
configurable: true,
value: function() { /* method body */ }
}
});
Object.defineProperty(C, "N", {
enumerable: true,
configurable:false, writable:false,
value: 1
});
Object Extensibility
- Object.preventExtensions()
- No new properties can be added
- Extensibility cannot be turned back on
- Does not affect prototype chain
- Object.isExtensible() tests extensibility
- Object extensibility and property configurablity are useful when
trying to sandbox untrusted code.
Lock Down
- Object.seal():
- Calls Object.preventExtensions().
- Makes all own properties non-configurable.
- Cannot be undone.
- Test with Object.isSealed().
- Object.freeze()
- Does everything Object.seal() does.
- And makes all own properties read-only.
- Test with Object.isFrozen().
Strict Mode
- Strict semantics, not strict syntax.
- Not a lint tool, but should prevent bugs.
- Not backward-compatible, so opt-in required.
- Use it for all new code.
- Not yet implemented
"use strict";
- Strict code is marked with a "use strict" directive: A
string literal as a no-op expression statement.
- Quotes to be dropped in a future version
- Other directives ("use caja", e.g.) are implementation-dependent.
- Must come before any real statement in a:
- File
- Function
- String passed to eval()
- Strictness is inherited from containing context
Strict Code runs in Strict Mode
- Strict code is executed in strict mode
- Non-strict code executes in non-strict mode.
- The strictness of the caller doesn't matter.
Important Strict Mode Changes
- No with statement
- Assigning to an undeclared variable is an error
- Does not automatically create a global var
- In functions, this is null, not the global object.
- This is critical for sandboxing untrusted code
- eval() evaluates code in a private scope and cannot
define new variables or functions in the calling scope.
- Assignments to read-only properties throw an error
- Attempts to delete non-configurable properties throw
Other Strict Mode Changes
- No octal integer literals
- eval and arguments are like keywords
- Can't assign to them
- Can't use them as variable or argument names
- arguments[] is no longer magic
- Now just a static copy of function arguments
- No arguments.caller and arguments.callee
- Object literals can't have two properties with the same name
- Functions can't have two arguments with the same name
Testing For Strict Mode
// Are we in strict mode?
var strict = (function() {
return !this;
}());
// Is strict mode supported?
var hasstrict = (function() {
"use strict";
return !this;
}());
Important eval() Change
- eval is like an operator
- Calling eval() using anything but the identifier "eval" is
an "indirect call".
- Indirect calls evaluate code in global scope
- var geval = eval; // geval() is a global eval function
- ES3 was allowed to throw EvalError on indirect calls
Miscellaneous Changes
- Regexp literals always create new objects
- undefined, NaN, and Infinity are read-only.
- parseInt() doesn't parse octal values.