advanced javascript
TRANSCRIPT
Advanced JavaScript:Advanced JavaScript:closures, prototypes, closures, prototypes, inheritanceinheritance
Stoyan StefanovAjax Experience, Boston 2008
About the presenter
• Yahoo! performance
team member
• YSlow 2.0 architect, dev
• Book author, open-source
contributor
• Blog: http://phpied.com
Firebug console…
• Inspect the contents of objects by clicking on
them
• Tab auto-complete, a.k.a cheatsheet
• Arrows and↑ ↓
• Fiddle with any page
What’s an object?
• a hash of key => value pairs
• if a key (property) happens to be a function,
we can call it a method
What’s an object?
var obj = {
shiny: true,
isShiny: function() {
return this.shiny;
}
};
obj.isShiny(); // true
Object literal notation
• { Wrapped in curly braces }
• ,-delimited properties
• key:value pairs
var obj = {a: 1, "b c d": 2};
Arrays
• array objects also get some cool properties...>>> a.length
3
• ...and methods>>> a.sort()
>>> a.join(' < ')
"1 < 2 < 3"
Array literal notation
var array = [
"Square", "brackets",
"wrap", "the",
"comma-delimited",
"elements"
];
JSON
• JavaScript Object Notation
• Uses object and array literals
• Quotes required for properties
{"num": 1, "str": "abc", "arr": [1,2,3]}
Functions
• functions are objects
• they have properties
• they have methods
• can de copied, deleted, augmented...
• special feature: invokable
Functions
function boo(what) {
return what;
}
• or
var boo = function bootoo(what) {
return what;
};
Functions are objects
>>> var foo = boo;
>>> foo("doodles")
"doodles"
>>> foo.call(null, "moo!");
"moo!"
Return value
• all functions return a value
• if they don't explicitly, they return
undefined implicitly
• functions can return other functions
Constructors
• when invoked with new, functions return an
object known as this
• you have a chance of modifying this before
it's returned
• you can also return some other object
Constructor functions
var Person = function(name) {
this.name = name;
this.speaks = 'fr';
this.say = function() {
return "Je m'appelle " + this.name;
};
};
An object created with constructor
>>> var julien = new Person("Julien");
>>> julien.say();
"Je m'appelle Julien"
Constructor’s return value
var Person = function(){
this.first = "Bruce";
return {last: "Wayne"};
};
>>> typeof new Person().first
"undefined"
>>> new Person().last
"Wayne"
Constructor’s return value
var Person = function(){
this.first = "Bruce";
return "Batman";
};
>>> new Person().first
"Bruce"
constructor property
>>> function Person(){};
>>> var jo = new Person();
>>> jo.constructor === Person
true
constructor property
>>> var o = {};
>>> o.constructor === Object
true
>>> [1,2].constructor === Array
true
Built-in constructor functions
• Object
• Array
• Function
• RegExp
• Number
• String
• Boolean
• Date
• Error, SyntaxError, ReferenceError…
var fn = new Function(
'a, b','return a+b');
var fn = function(a, b){
return a + b;
}
var re = new RegExp(
'[a-z]', 'gmi');
var re = /[a-z]/gmi;
var a = new Array();var a = [];
var o = new Object();var o = {};
Not thatUse this
prototype
• a property of the function objects
>>> var boo = function(){};
>>> typeof boo.prototype
"object"
How is the prototype used?
• when a function is invoked as a constructor
var Person = function(name) {
this.name = name;
};
Person.prototype.say = function() {
return this.name;
}
>>> var dude = new Person('dude');
>>> dude.name;
"dude"
>>> dude.say();
"dude"
How is the prototype used?
• say() is a property of the prototype
object
• but it behaves as if it's a property of the dude
object
• can we tell the difference?
How is the prototype used?
Own properties vs. prototype’s
>>> dude.hasOwnProperty('name');
true
>>> dude.hasOwnProperty('say');
false
isPrototypeOf()
>>> Person.prototype.isPrototypeOf(dude);
true
>>> Object.prototype.isPrototypeOf(dude);
true
__proto__
• I, the dude, have a secret link to the
prototype of the constructor that created me
• __proto__ is not directly exposed in all
browsers
>>> dude.__proto__.hasOwnProperty('say')
true
>>> dude.prototype
??? // Trick question
>>> dude.__proto__.__proto__.hasOwnProperty('toString')
true
__proto__
It’s alive!
>>> typeof dude.numlegs
"undefined"
>>> Person.prototype.numlegs = 2;
>>> dude.numlegs
2
Inheritance via the prototype
>>> var Dad = function(){this.family = "Stefanov";};
>>> var Kid = function(){};
>>> Kid.prototype = new Dad();
>>> var billy = new Kid();
>>> billy.family
"Stefanov"
Inherit one more time
>>> var GrandKid = function(){};
>>> GrandKid.prototype = billy;
>>> var jill = new GrandKid();
>>> jill.family
"Stefanov"
Inheritance…
>>> jill.hasOwnProperty('family')
false
>>> jill.__proto__.hasOwnProperty('family')
false
>>> jill.__proto__.__proto__.hasOwnProperty('family')
true
Inheritance…
>>> billy.family = 'Idol';
>>> jill.family;
'Idol'
>>> jill.__proto__.hasOwnProperty('family');
true
>>> delete billy.family;
>>> jill.family;
'Stefanov'
Side effect… easy to solve
• reset after inheritance
>>> Kid.prototype.constructor = Kid;
>>> GrandKid.prototype.constructor =
GrandKid;
instanceof
>>> jill instanceof GrandKid
true
>>> jill instanceof Kid
true
>>> jill instanceof Dad
true
Classes?
• There are no classes in JavaScript
• Objects inherit from objects
• classical inheritance is when we think of
constructors as if they were classes
Classical inheritance
function Parent(){this.name = 'parent';}
Parent.prototype.getName = function(){
return this.name;
};
function Child(){}
inherit(Child, Parent);
Option 3
function inherit(C, P) {
var F = function(){};
F.prototype = P.prototype;
C.prototype = new F();
}
Option 3 + super
function inherit(C, P) {
var F = function(){};
F.prototype = P.prototype;
C.prototype = new F();
C.uber = P.prototype;
}
Option 3 + super + constructor reset
function inherit(C, P) {
var F = function(){};
F.prototype = P.prototype;
C.prototype = new F();
C.uber = P.prototype; // super
C.prototype.constructor = C; // reset
}
Inheritance by copying properties
• After all, inheritance is all about code reuse
function extend(parent) {
var i, child = {};
for (i in parent) {
child[i] = parent[i];
}
return child;
}
Inheritance by copying…
• This was a shallow copy
• you can make a deep copy using recursion
• mixins / multiple inheritance
Prototypal inheritance
• as suggested by Douglas Crockford
• no class-like constructors involved
• objects inherit from objects
• via the prototype
Prototypal inheritance
>>> var parent = {a: 1};
>>> var child = object(parent);
>>> child.a;
1
>>> child.hasOwnProperty(a);
false
Global namespace
• every variable is global unless it's in a
function and is declared with var
• global namespace should be kept clean to
avoid naming collisions
• function scope can help
Closure example #2
var inner;
function outer(){
var local = 1;
inner = function(){
return local;
};
}
Closure #4 – in a loop
function make() {
var i, a = [];
for(i = 0; i < 3; i++) {
a[i] = function(){
return i;
}
}
return a;
}
Closure #4 test - oops
>>> var funcs = make();
>>> funcs[0]();
3
>>> funcs[1]();
3
>>> funcs[2]();
3
Closure #4 – corrected
function make() {
var i, a = [];
for(i = 0; i < 3; i++) {
a[i] = (function(local){
return function(){return local;}
})(i)
}
return a;
}
Getter/Setter
var getValue, setValue;
(function() {
var secret = 0;
getValue = function(){
return secret;
};
setValue = function(v){
secret = v;
};
})()
// usage>>> getValue()0>>> setValue(123)>>> getValue()123
Loop through DOM elements - wrong
// all elements will alert 5
for (var i = 1; i < 5; i++ ){
document.getElementById('btn'+i).onclick =
function(){
alert(i);
};
}
Loop through DOM elements - correct
// first element alerts 1, second 2,...
for (var i = 1; i < 5; i++ ){
document.getElementById('btn'+i).onclick =
(function(i){
return function(){alert(i);};
})(i)
}
>>> typeof variable
• typeof is an operator, not a function
• Not typeof(variable) even if it works
• Returns a string, one of:
"string", "number", "boolean",
"undefined", "object", "function"
typeof
if (typeof whatever === "undefined") {
// whatever is not defined
}
if (whatever == undefined) {
// hmm, not so sure
}
>>> obj instanceof MyConstructor
• Not instanceof()
• Returns true | false
• true for all constructors up the chain
obj.propertyIsEnumerable("prop")
• Will it show up in a for-in loop
• Caution: enumerable properties of the
prototype will return false but still show up
in the for-in loop
Objects
• JavaScript has a few primitive types,
everything else is an object
• Objects are hashes
• Arrays are objects
Prototype
• Functions have a prototype property which is
an object
• Useful with Constructor functions