advanced javascript: closures, prototypes, inheritance stoyan stefanov ajax experience, boston 2008

Post on 18-Dec-2015

222 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

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

Thank you!

Stoyan Stefanov

http://www.phpied.com

top related