Skip to main content
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'] oldObject newObject 2 foo 1 foo
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). oldObject myDoppelganger = object(newObject); newObject
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 (); Constructor .prototype newObject
new  operator The  Constructor ()  function is passed the new object in the  this  variable. This allows the  Constructor  function to customize the new object. Constructor .prototype newobject
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) { this.id = id; } Gizmo.prototype.toString = function () { return "gizmo " + this.id; }; new Gizmo( string ) Gizmo Object prototype string id function toString constructor prototype function toString constructor
Example function Gizmo(id) { this.id = id; } Gizmo.prototype.toString = function () { return "gizmo " + this.id; }; new Gizmo( string ) Gizmo Object prototype string id function toString constructor prototype function toString constructor
Example function Gizmo(id) { this.id = id; } Gizmo.prototype.toString = function () { return "gizmo " + this.id; }; new Gizmo( string ) Gizmo Object prototype string id function toString constructor prototype function toString constructor
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; } Hoozit.prototype = new Gizmo(); Hoozit.prototype.test = function (id) { return this.id === id; }; Gizmo Hoozit new Hoozit( string ) prototype prototype function test constructor function toString constructor string id
Example function Hoozit(id) { this.id = id; } Hoozit.prototype = new Gizmo(); Hoozit.prototype.test = function (id) { return this.id === id; }; Gizmo Hoozit new Hoozit( string ) prototype prototype function test constructor function toString constructor string id
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 F function object(o) { function F() {} F.prototype = o; return new F(); }  newobject = object(oldobject) prototype constructor
object  function F function object(o) { function F() {} F.prototype = o; return new F(); }  newobject = object(oldobject) oldobject prototype constructor
object  function F newobject function object(o) { function F() {} F.prototype = o; return new F(); }  newobject = object(oldobject) oldobject prototype
object  function newobject function object(o) { function F() {} F.prototype = o; return new F(); }  newobject = object(oldobject) 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. Make a new object somehow. Augment it. 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) { return { id: s,  lbp: p,  value: s }; } function delim(s) { return symbol(s, 0); } function stmt(s, f) { var x = delim(s); x.identifier = true; x.reserved = true; x.fud = f; return x; } function blockstmt(s, f) { 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  (n 2 ) 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 = &quot;black&quot;;  divs[i].style. border =  thickness +  'px solid blue' ;  divs[i].style. backgroundColor = &quot;white&quot;;  }
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 = &quot;black&quot;;  ds .border =  border ;  ds .backgroundColor = &quot;white&quot;;  }
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
Array
Value
String
Number
Advanced JavaScript Douglas Crockford © 2006 Douglas Crockford