advanced javascript: closures, prototypes, inheritance stoyan stefanov ajax experience, boston 2008
Post on 18-Dec-2015
222 Views
Preview:
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
Before we start… Firebug console
Firebug console is a learning tool
Firebug console…
• Inspect the contents of objects by clicking on
them
• Tab auto-complete, a.k.a cheatsheet
• Arrows ↑ and↓
• Fiddle with any page
Any page…
Fiddle…
Objects
JavaScript data types
primitive and objects
• number
• string
• boolean
• undefined
• null
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
Arrays
• arrays are also objects
• auto-incremented properties
Arrays
>>> var a = [1,3,2];
>>> a[0]
1
>>> typeof a
"object"
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
• 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(what) {
return what;
};
Functions
function boo(what) {
return what;
}
• or
var boo = function bootoo(what) {
return what;
};
Functions are objects
>>> boo.length
1
>>> boo.name
"bootoo"
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
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"
Naming convention
• MyConstructor
• myFunction
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…
Use this Not that
var o = {}; var o = new Object();
var a = []; var a = new Array();
var re = /[a-z]/gmi; var re = new RegExp(
'[a-z]', 'gmi');
var fn = function(a, b){
return a + b;
}
var fn = new Function(
'a, b','return a+b');
Wrapper objects vs. primitive
>>> typeof new Number(1)
"object"
>>> typeof 1
"number"
Primitives can act as objects
>>> "test".length
4
>>> (123.456).toFixed(2)
"123.46"
Prototype
prototype
• a property of the function objects
>>> var boo = function(){};
>>> typeof boo.prototype
"object"
Prototypes can be augmented
>>> boo.prototype.a = 1;
>>> boo.prototype.sayAh = function(){};
Prototypes can be overwritten
>>> boo.prototype = {a: 1, b: 2};
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__
The prototype chain
It’s alive!
>>> typeof dude.numlegs
"undefined"
>>> Person.prototype.numlegs = 2;
>>> dude.numlegs
2
Inheritance
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…
>>> billy.constructor === Kid
false
>>> billy.constructor === Dad
true
Side effect… easy to solve
• reset after inheritance
>>> Kid.prototype.constructor = Kid;
>>> GrandKid.prototype.constructor =
GrandKid;
isPrototypeOf
>>> billy.isPrototypeOf(jill)
true
>>> Kid.prototype.isPrototypeOf(jill)
true
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 1
function inherit(C, P) {
C.prototype = new P();
}
Option 2
function inherit(C, P) {
C.prototype = P.prototype;
}
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…
>>> var parent = {a: 1};
>>> var child = extend(parent);
>>> child.a
1
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
function object(o) {
function F(){}
F.prototype = o;
return new F();
}
Prototypal inheritance
>>> var parent = {a: 1};
>>> var child = object(parent);
>>> child.a;
1
>>> child.hasOwnProperty(a);
false
Scope
No block scope
>>> if (true) {var inside_block = 1;}
>>> inside_block
1
Function scope
function boo() {
var inboo = true;
}
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
Self-executable functions for one-off tasks
(function(){
var a = 1;
var b = 2;
alert(a + b);
})()
Closures
Closure example #1
function outer(){
var local = 1;
return function(){
return local;
};
}
example #1…
>>> var inner = outer()
>>> inner()
1
Closure example #2
var inner;
function outer(){
var local = 1;
inner = function(){
return local;
};
}
example #2…
>>> typeof inner
"undefined"
>>> outer()
>>> inner()
1
Closure example #3
function makePlus(arg) {
var n = function(){
return arg;
};
arg++;
return n;
}
example #3…
>>> var getValue = makePlus(1234);
>>> getValue()
1235
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
Iterator
function setup(x) {
var i = 0;
return function(){
return x[i++];
};
}
Iterator usage
>>> var next = setup(['a', 'b', 'c']);
>>> next()
'a'
>>> next()
'b'
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)
}
Wrapping up…
• How to tell what’s going on?
typeof, instanceof, isPrototypeOf()…
>>> 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.constructor
• Points to the constructor function used to
create this obj
>>> obj.isPrototypeOf(child_obj)
• Respects the prototype chain
>>> obj.hasOwnProperty("prop")
• Own properties vs. properties of the
prototype
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
Wrapping up…
• What did we learn today?
Objects
• JavaScript has a few primitive types,
everything else is an object
• Objects are hashes
• Arrays are objects
Functions
• Functions are objects, only invokable
• call() and apply() methods
• prototype property
Prototype
• Functions have a prototype property which is
an object
• Useful with Constructor functions
Constructor
• A function meant to be called with new
• Returns an object
Class
• No such thing in JavaScript
Inheritance
• Prototypal
• Classical
• … and approximately 101 other ways and
variations
Scope
• Lexical function scope
Closure
• When a variable leaves its function scope
top related