Advanced JavaScript
Douglas Crockford
© 2006 Douglas Crockford
Coming Up
• Inheritance
• Modules
• Debugging
• Efficiency
• JSON
Inheritance
• Inheritance is object-oriented
code reuse.
• Two Schools:
• Classical
• Prototypal
Classical Inheritance
• Objects are instances of Classes.
• A Class inherits from another
Class.
Prototypal Inheritance
• Class-free.
• Objects inherit from objects.
• An object contains a secret link to
another object.
• Mozilla calls it __proto__.
var newObject = object(oldObject);
newObject oldObject
__proto__
Prototypal Inheritance
var oldObject = {
firstMethod: function () {...},
secondMethod: function () {...}
};
var newObject = object(oldObject);
newObject.thirdMethod = function () {...};
var myDoppelganger = object(newObject);
myDoppelganger.firstMethod();
Prototypal Inheritance
• If an object has a foo property,
then the chain will not be
consulted when accessing
member foo.
newObject.foo newObject['foo']
newObject oldObject
foo 2 foo 1
Prototypal Inheritance
• If access of a member of
newObject fails, then search for
the member in oldObject.
• If that fails, then search for the
member in Object.prototype.
newObject oldObject
Prototypal Inheritance
• Changes in oldObject may be
immediately visible in newObject.
• Changes to newObject have no
effect on oldObject.
newObject oldObject
Prototypal Inheritance
• oldObject can be the prototype
for an unlimited number of objects
which will all inherit its
properties.
newObject oldObject
Prototypal Inheritance
• newObject can be the prototype
for an unlimited number of even
newer objects.
• There is no limit to the length of
the chain (except common sense).
myDoppelganger = object(newObject);
newObject oldObject
Augmentation
• Using the object function, we can
quickly produce new objects that
have the same state and behavior
as existing objects.
• We can then augment each of the
instances by assigning new
methods and members.
Pseudoclassical
• A prototypal inheritance language
should have an operator like the object
function, which makes a new object
using an existing object as its
prototype.
• JavaScript instead uses operators that
look classical, but behave prototypally.
• They tried to have it both ways.
Pseudoclassical
• Three mechanisms:
• Constructor functions.
• The new operator.
• The prototype member of functions.
new operator
function Constructor() {
this.member = initializer;
return this; // optional
}
Constructor.prototype.firstMethod =
function (a, b) {...};
Constructor.prototype.secondMethod =
function (c) {...};
var newobject = new Constructor();
Constructor
• When functions are designed to be used
with new, they are called constructors.
• Constructors are used to make objects of
a type or class.
• JavaScript's notation can get a little
strange because it is trying to look like
the old familiar classical pattern, while
also trying to be something really
different.
new operator
• new Constructor() returns a new
object with a link to
Constructor.prototype.
var newObject = new Constructor();
newObject Constructor.prototype
new operator
• The Constructor() function is
passed the new object in the this
variable.
• This allows the Constructor
function to customize the new
object.
newobject Constructor.prototype
Warning
• The new operator is required when
calling a Constructor.
• If new is omitted, the global object
is clobbered by the constructor,
and then the global object is
returned instead of a new
instance.
prototype
• When a function object is
created, it is given a
prototype member which is
an object containing a
constructor member which
is a reference to the
function object.
prototype
• You can add other members to a
function's prototype. These members
will be linked into objects that are
produced by calling the function with
the new operator.
• This allows for adding constants and
methods to every object produced,
without the objects having to be
enlarged to contain them.
• Differential Inheritance.
method method
Function.prototype.method =
function (name, func) {
this.prototype[name] = func;
return this;
};
Constructor.
method('first_method',
function (a, b) {...}).
method('second_method',
function (c) {...});
Pseudoclassical Inheritance
• Classical inheritance can be
simulated by assigning an object
created by one constructor to the
prototype member of another.
• This does not work exactly like
the classical model.
function BiggerConstructor() {};
BiggerConstructor.prototype =
new MyConstructor();
Example
function Gizmo(id) {
this.id = id;
}
Gizmo.prototype.toString = function () {
return "gizmo " + this.id;
};
Example
function Gizmo(id) { new Gizmo(string)
this.id = id;
id string
}
Gizmo.prototype.toString = function () {
return "gizmo " + this.id;
};
Gizmo
constructor
prototype toString function
Object
constructor
prototype
toString function
Example
function Gizmo(id) { new Gizmo(string)
this.id = id;
id string
}
Gizmo.prototype.toString = function () {
return "gizmo " + this.id;
};
Gizmo
constructor
prototype toString function
Object
constructor
prototype
toString function
Example
function Gizmo(id) { new Gizmo(string)
this.id = id;
id string
}
Gizmo.prototype.toString = function () {
return "gizmo " + this.id;
};
Gizmo
constructor
prototype toString function
Object
constructor
prototype
toString function
Inheritance
• If we replace the original
prototype object with an instance
of an object of another class, then
we can inherit another class's
stuff.
Example
function Hoozit(id) {
this.id = id;
}
Hoozit.prototype = new Gizmo();
Hoozit.prototype.test = function (id) {
return this.id === id;
};
Example
function Hoozit(id) {
this.id = id; new Hoozit(string)
}
Hoozit.prototype = new Gizmo();
Hoozit.prototype.test = function (id) { id string
return this.id === id;
};
Gizmo
constructor
prototype toString function
Hoozit
constructor
prototype
test function
Example
function Hoozit(id) {
this.id = id; new Hoozit(string)
}
Hoozit.prototype = new Gizmo();
Hoozit.prototype.test = function (id) { id string
return this.id === id;
};
Gizmo
constructor
prototype toString function
Hoozit
constructor
prototype
test function
object function
• A prototypal inheritance language
should have an operator like the
object function, which makes a
new object using an existing
object as its prototype.
object function
function object(o) {
function F() {}
F.prototype = o;
return new F();
}
object function
function object(o) {
function F() {}
F.prototype = o;
return new F();
}
newobject = object(oldobject)
F
prototype constructor
object function
function object(o) {
function F() {}
F.prototype = o;
return new F();
}
newobject = object(oldobject)
F
prototype constructor
oldobject
object function
function object(o) {
function F() {}
F.prototype = o;
return new F();
}
newobject = object(oldobject)
F
prototype
newobject oldobject
object function
function object(o) {
function F() {}
F.prototype = o;
return new F();
}
newobject = object(oldobject)
newobject oldobject
Public Method
• A Public Method is a function that
uses this to access its object.
• A Public Method can be reused
with many "classes".
Public Methods
function (string) {
return this.member + string;
}
• We can put this function in any object
at it works.
• Public methods work extremely well
with prototypal inheritance and with
pseudoclassical inheritance.
Singletons
• There is no need to produce a
class-like constructor for an
object that will have exactly one
instance.
• Instead, simply use an object
literal.
Singletons
var singleton = {
firstMethod: function (a, b) {
...
},
secondMethod: function (c) {
...
}
};
Singletons
• The methods of a singleton can
enjoy access to shared private
data and private methods.
Functions
Functions are used as
• Functions
• Methods
• Constructors
• Classes
• Modules
Module
• Variables defined in a module are
only visible in the module.
• Functions have scope.
• Variables defined in a function
only visible in the function.
• Functions can be used a module
containers.
Global variables are evil
• Functions within an application
can clobber each other.
• Cooperating applications can
clobber each other.
• Use of the global namespace
must be minimized.
Singletons
var singleton = function () {
var privateVariable;
function privateFunction(x) {
...privateVariable...
}
return {
firstMethod: function (a, b) {
...privateVariable...
},
secondMethod: function (c) {
...privateFunction()...
}
};
}();
Applications are Singletons
YAHOO.MyProperty = function () {
var privateVariable;
function privateFunction(x) {
...privateVariable...
}
return {
firstMethod: function (a, b) {
...privateVariable...
},
secondMethod: function (c) {
...privateFunction()...
}
};
}();
Privileged Method
• A Privileged Method is a function that
has access to secret information.
• A Privileged Method has access to
private variables and private methods.
• A Privileged Method obtains its secret
information through closure.
Power Constructor
• Put the singleton module pattern
in constructor function, and we
have a power constructor
pattern.
3. Make a new object somehow.
5. Augment it.
7. Return it.
function powerConstructor() {
var that = object(oldObject),
privateVariable;
function privateFunction(x) {}
that.firstMethod = function (a, b) {
...privateVariable...
};
that.secondMethod = function (c) {
...privateFunction()...
};
return that;
}
Power Constructor
• Public methods (from the
prototype)
var that = object(my_base);
• Private variables (var)
• Private methods (inner functions)
• Privileged methods (that...)
• No need to use new
myObject = power_constructor();
Parasitic Inheritance
• A power constructor calls another
constructor, takes the result,
augments it, and returns it as
though it did all the work.
function symbol(s, p) { function stmt(s, f) {
return { var x = delim(s);
id: s, x.identifier = true;
lbp: p, x.reserved = true;
value: s x.fud = f;
}; return x;
} }
function delim(s) { function blockstmt(s, f) {
return symbol(s, 0); var x = stmt(s, f);
} x.block = true;
return x;
}
Pseudoclassical Inheritance
function Gizmo(id) {
this.id = id;
}
Gizmo.prototype.toString = function () {
return "gizmo " + this.id;
};
function Hoozit(id) {
this.id = id;
}
Hoozit.prototype = new Gizmo();
Hoozit.prototype.test = function (id) {
return this.id === id;
};
Parasitic Inheritance
function gizmo(id) {
return {
id: id,
toString: function () {
return "gizmo " + this.id;
}
};
}
function hoozit(id) {
var that = gizmo(id);
that.test = function (testid) {
return testid === this.id;
};
return that;
}
Secrets
function gizmo(id) {
return {
toString: function () {
return "gizmo " + id;
}
};
}
function hoozit(id) {
var that = gizmo(id);
that.test = function (testid) {
return testid === id;
};
return that;
}
Shared Secrets
function gizmo(id, secret) {
secret = secret || {};
secret.id = id;
return {
toString: function () {
return "gizmo " + secret.id;
};
};
}
function hoozit(id) {
var secret = {}, /*final*/
that = gizmo(id, secret);
that.test = function (testid) {
return testid === secret.id;
};
return that;
}
Super Methods
function hoozit(id) {
var secret = {},
that = gizmo(id, secret),
super_toString = that.toString;
that.test = function (testid) {
return testid === secret.id;
};
that.toString = function () {
return super_toString.apply(that,
[]);
};
return that;
}
Inheritance Patterns
• Prototypal Inheritance works
really well with public methods.
• Parasitic Inheritance works really
well with privileged and private
and public methods.
• Pseudoclassical Inheritance for
elderly programmers who are old
and set in their ways.
Working with the Grain
• Pseudoclassical patterns are less
effective than prototypal patterns
or parasitic patterns.
• Formal classes are not needed for
reuse or extension.
• Be shallow. Deep hierarchies are
not effective.
later method
• The later method causes a
method on the object to be
invoked in the future.
my_object.later(1000, "erase", true);
later method
Object.prototype.later =
function (msec, method) {
var that = this,
args = Array.prototype.slice.
apply(arguments, [2]);
if (typeof method === 'string') {
method = that[method];
}
setTimeout(function () {
method.apply(that, args);
}, msec);
return that;
};
Multiples
• When assigning functions in a
loop, be aware that all of the
functions are bound to the same
closure.
• This can be avoided by using a
factor function to produce unique
bindings.
Multiples
for (i ...) {
var div_id = divs[i].id;
divs[i].onmouseover = function () {
show_element_id(div_id);
};
}
for (i ...) {
var div_id = divs[i].id;
divs[i].onmouseover = function (id) {
return function () {
show_element_id(id);
};
}(div_id);
}
Debugging
• As programs get larger and more
complex, debugging tools are
required for efficient
development.
Debugging
• IE
Microsoft Script Debugger
Office 2003
Visual Studio
• Mozilla
Venkman
Firebug
• Safari
Drosera
Microsoft Script Debugger
Microsoft Script Debugger
Microsoft Script Debugger
Microsoft Script Debugger
Venkman
Venkman
Venkman
debugger
• The debugger statement can be
used as a programmable
breakpoint.
if (something === 'wrong') {
debugger;
}
Performance
• Provide a good experience.
• Be respectful of our customer's
time.
• Hoare's Dictum: Premature
optimization is the root of all evil.
Efficiency
• The first priority must always be
correctness.
• Optimize when necessary.
• Consider algorithmic improvements
O (n) v O (n log n) v O (n2)
• Watch for limits.
Coding Efficiency
• Common subexpression removal
• Loop invariant removal
Before
for (var i = 0; i < divs.length; i += 1) {
divs[i].style.color = "black";
divs[i].style.border = thickness +
'px solid blue';
divs[i].style.backgroundColor = "white";
}
After
var border = thickness + 'px solid blue',
nrDivs = divs.length;
for (var i = 0; i < nrDivs; i += 1) {
var ds = divs[i].style;
ds.color = "black";
ds.border = border;
ds.backgroundColor = "white";
}
Strings
• Concatenation with +
Each operation allocates memory
foo = a + b;
• Concatenate with array.join('')
The contents of an array are
concatenated into a single string
foo = [a, b].join('');
Minification vs
Obfuscation
• Reduce the amount of source
code to reduce download time.
• Minification deletes whitespace
and comments.
• Obfuscation also changes the
names of things.
• Obfuscation can introduce bugs.
• Never use tools that cause bugs if
you can avoid it.
http://www.crockford.com/javascript/jsmin.html
JSON
• JavaScript Object Notation.
• A Data Interchange Format.
• Text-based.
• Light-weight.
• Easy to parse.
• Language Independent.
• A Subset of ECMA-262
ECMAScript Third Edition.
Object
object
{ string : value }
,
Array
array
[ value ]
,
Value
value
string
number
object
array
true
false
null
String
string
A ny U N IC O D E character except
" "
" or \ or controlcharacter
quotation m ark
\ "
reverse solidus
\
solidus
/
backspace
b
form feed
f
new line
n
carriage return
r
horizontaltab
t
u 4 hexadecim aldigits
Number
number
0 . digit
-
digit
e
1 -9
digit E
+
digit
-
Advanced JavaScript
Douglas Crockford
© 2006 Douglas Crockford