the many facets of code reuse in javascript

110
The many facets of code reuse in JavaScript Leonardo Borges @leonardo_borges http://www.leonardoborges.com http://www.thoughtworks.com

Upload: leonardo-borges

Post on 20-May-2015

2.304 views

Category:

Technology


1 download

DESCRIPTION

Talk given at the ThoughtWorks XConf event in Australia - April/2012

TRANSCRIPT

Page 1: The many facets of code reuse in JavaScript

The many facets of code reuse in JavaScript

Leonardo Borges@leonardo_borgeshttp://www.leonardoborges.comhttp://www.thoughtworks.com

Page 2: The many facets of code reuse in JavaScript

Highly opinionated talk ahead

Page 3: The many facets of code reuse in JavaScript

JavaScript is all about Objects

Page 4: The many facets of code reuse in JavaScript

Not classes

Page 5: The many facets of code reuse in JavaScript

Not access modifiers

Page 6: The many facets of code reuse in JavaScript

Just Objects

Page 7: The many facets of code reuse in JavaScript

Objects

• An unordered collection of properties• Arrays are Objects• Functions are Objects• Regular Expressions are Objects

...you catch my drift

Page 8: The many facets of code reuse in JavaScript

Ok, I lied.

Page 9: The many facets of code reuse in JavaScript

There’s more to JavaScript than just Objects

Page 10: The many facets of code reuse in JavaScript

The Prototype

Page 11: The many facets of code reuse in JavaScript

The Prototype

Object

- prototype- __proto__

Page 12: The many facets of code reuse in JavaScript

The Prototype

Object

- prototype- __proto__

Page 13: The many facets of code reuse in JavaScript

The Prototype

myObject

- prototype- __proto__

Object

- prototype- __proto__

Page 14: The many facets of code reuse in JavaScript

The Prototype

myObject

- prototype- __proto__

Object

- prototype- __proto__

Page 15: The many facets of code reuse in JavaScript

The Prototype

myObject

- prototype- __proto__

Object

- prototype- __proto__

undefined

Page 16: The many facets of code reuse in JavaScript

The Prototype

myObject

- prototype- __proto__

Object

- prototype- __proto__

undefined function Empty() {}

Page 17: The many facets of code reuse in JavaScript

But how do you create Objects?

Page 18: The many facets of code reuse in JavaScript

Object Literals

var person = {    firstName: "Leonardo",    lastName: "Borges",    age: 29};

Page 19: The many facets of code reuse in JavaScript

Object.create

• Specified by the ECMAScript 5th edition • Modern browsers such as Chrome and Firefox already implement it

Page 20: The many facets of code reuse in JavaScript

Object.create

• Specified by the ECMAScript 5th edition • Modern browsers such as Chrome and Firefox already implement it

var anotherPerson = Object.create(person, {    age: { value: 50 }});anotherPerson.age; //50

Page 21: The many facets of code reuse in JavaScript

Object.createif (typeof Object.create !== 'function') {    Object.create = function(o) {        var F = function() {};        F.prototype = o;        return new F();    };}

var anotherPerson = Object.create(person);

Page 22: The many facets of code reuse in JavaScript

Functions

• First class objects• Linked to Function.prototype• Can be used as constructors f(x)

Page 23: The many facets of code reuse in JavaScript

Functions

Function

- prototype- __proto__

Page 24: The many facets of code reuse in JavaScript

Functions

Function

- prototype- __proto__

function Empty() {}

Page 25: The many facets of code reuse in JavaScript

Functions

myFunction

- prototype- __proto__

Function

- prototype- __proto__

function Empty() {}

Page 26: The many facets of code reuse in JavaScript

Functions

myFunction

- prototype- __proto__

Function

- prototype- __proto__

function Empty() {}

Page 27: The many facets of code reuse in JavaScript

Functions

myFunction

- prototype- __proto__

Function

- prototype- __proto__

function Empty() {}

Page 28: The many facets of code reuse in JavaScript

Function calls

Implicit arguments:

• this, the current object• arguments, an array containing all values passed into the function call

Page 29: The many facets of code reuse in JavaScript

function f(a, b, c) {    console.log(a);    console.log(b);    console.log(c);    console.log(this);    console.log(arguments);}f(1, 2, 3);

Page 30: The many facets of code reuse in JavaScript

function f(a, b, c) {    console.log(a);    console.log(b);    console.log(c);    console.log(this);    console.log(arguments);}f(1, 2, 3);

// 1// 2// 3// DOMWindow// [1, 2, 3]

Page 31: The many facets of code reuse in JavaScript

function f(a, b, c) {    console.log(a);    console.log(b);    console.log(c);    console.log(this);    console.log(arguments);}f(1, 2, 3);

// 1// 2// 3// DOMWindow// [1, 2, 3]

The value of this changes depending on how the function is called.

Page 32: The many facets of code reuse in JavaScript

Calling functions: as methodsvar person = {    firstName: "Leonardo",    lastName: "Borges",    fullName: function() {        return this.firstName + " " + this.lastName;    }};

person.fullName();

Page 33: The many facets of code reuse in JavaScript

Calling functions: as methodsvar person = {    firstName: "Leonardo",    lastName: "Borges",    fullName: function() {        return this.firstName + " " + this.lastName;    }};

person.fullName();

this is bound to person

Page 34: The many facets of code reuse in JavaScript

Calling functions: as, err, functionsthis.firstName = "Leo";this.lastName = "Borges";

function fullName() {    return this.firstName + " " + this.lastName;}

fullName(); //Leo Borgesthis; //DOMWindow

Page 35: The many facets of code reuse in JavaScript

Calling functions: as, err, functionsthis.firstName = "Leo";this.lastName = "Borges";

function fullName() {    return this.firstName + " " + this.lastName;}

fullName(); //Leo Borgesthis; //DOMWindow

this is bound to the global object

Page 36: The many facets of code reuse in JavaScript

Calling functions: using apply

Allows the value of this to be changed upon calling:

Page 37: The many facets of code reuse in JavaScript

Calling functions: using apply

var anotherPerson = {    firstName: "Johnny",    lastName: "Cash"};

person.fullName.apply(anotherPerson); //Johnny Cash

Allows the value of this to be changed upon calling:

Page 38: The many facets of code reuse in JavaScript

Calling functions: using apply

var anotherPerson = {    firstName: "Johnny",    lastName: "Cash"};

person.fullName.apply(anotherPerson); //Johnny Cash

Allows the value of this to be changed upon calling:

this is bound to anotherPerson

Page 39: The many facets of code reuse in JavaScript

Calling functions: constructors

//constructor functionvar F = function() {};

var obj = new F();

Page 40: The many facets of code reuse in JavaScript

What does new do?

Creates a new object, obj

Assigns F’s public prototype to the obj internal prototype

Binds this to obj

var obj = {};

obj.__proto__ === F.prototype// true

this === obj// true

Page 41: The many facets of code reuse in JavaScript

Don’t use new

Page 42: The many facets of code reuse in JavaScript

Don’t use new

• No built-in checks to prevent constructors from being called as regular functions• If you forget new, this will be bound to the global object

Page 43: The many facets of code reuse in JavaScript

But I want to

Page 44: The many facets of code reuse in JavaScript

new workaround//constructor functionvar F = function() {    if (!(this instanceof F)) {        return new F();    }};

var obj = new F();var obj = F();//both previous statements are now equivalent

Page 45: The many facets of code reuse in JavaScript

new workaround//constructor functionvar F = function() {    if (!(this instanceof F)) {        return new F();    }};

var obj = new F();var obj = F();//both previous statements are now equivalent

You can see how cumbersome this can get

Page 46: The many facets of code reuse in JavaScript

Closures

var Person = function(name) {    this.name = name;    return {        getName: function() {            return this.name;        }    };};var leo = new Person("leo");leo.getName();

Page 47: The many facets of code reuse in JavaScript

Closures

var Person = function(name) {    this.name = name;    return {        getName: function() {            return this.name;        }    };};var leo = new Person("leo");leo.getName();

Can you guess what this line returns?

Page 48: The many facets of code reuse in JavaScript

undefined

Page 49: The many facets of code reuse in JavaScript
Page 50: The many facets of code reuse in JavaScript

Closures

var Person = function(name) {    this.name = name;    return {        getName: function() {            return this.name;        }    };};var leo = new Person("leo");leo.getName();

Page 51: The many facets of code reuse in JavaScript

Closures

Bound to the person object

var Person = function(name) {    this.name = name;    return {        getName: function() {            return this.name;        }    };};var leo = new Person("leo");leo.getName();

Page 52: The many facets of code reuse in JavaScript

Closures

Bound to the person object

Bound to the object literal

var Person = function(name) {    this.name = name;    return {        getName: function() {            return this.name;        }    };};var leo = new Person("leo");leo.getName();

Page 53: The many facets of code reuse in JavaScript

Closures

Allows a function to access variables outside it’s scope

Page 54: The many facets of code reuse in JavaScript

Closuresvar Person = function(name) {    this.name = name;    var that = this;    return {        getName: function() {            return that.name;        }    };};

var leo = new Person("leo");leo.getName(); // “leo”

Page 55: The many facets of code reuse in JavaScript

Closuresvar Person = function(name) {    this.name = name;    var that = this;    return {        getName: function() {            return that.name;        }    };};

var leo = new Person("leo");leo.getName(); // “leo”

{getName is now a closure: it closes over that

Page 56: The many facets of code reuse in JavaScript

Sharing behaviour

Page 57: The many facets of code reuse in JavaScript

Pseudoclassical inheritance//constructor functionvar Aircraft = function(name){    this.name = name;};Aircraft.prototype.getName = function() {    return this.name;}Aircraft.prototype.fly = function() {    return this.name + ": Flying...";}

Page 58: The many facets of code reuse in JavaScript

Pseudoclassical inheritance//constructor functionvar Aircraft = function(name){    this.name = name;};Aircraft.prototype.getName = function() {    return this.name;}Aircraft.prototype.fly = function() {    return this.name + ": Flying...";}

var cirrus = new Aircraft("Cirrus SR22"); cirrus.getName(); //"Cirrus SR22"cirrus.fly(); //"Cirrus SR22: Flying..."

Page 59: The many facets of code reuse in JavaScript

Pseudoclassical inheritancevar Jet = function(name){    this.name = name;};Jet.prototype = new Aircraft();Jet.prototype.fly = function() {    return this.name + ": Flying a jet...";}

Page 60: The many facets of code reuse in JavaScript

Pseudoclassical inheritancevar Jet = function(name){    this.name = name;};Jet.prototype = new Aircraft();Jet.prototype.fly = function() {    return this.name + ": Flying a jet...";}

var boeing = new Jet("Boeing 747");boeing.getName(); //"Boeing 747"boeing.fly(); //"Boeing 747: Flying a jet..."

Page 61: The many facets of code reuse in JavaScript

Prototypal inheritance

• Objects inherit directly from other objects• Sometimes referred to as differential inheritance

Page 62: The many facets of code reuse in JavaScript

Prototypal inheritancevar myAircraft = {    name: "Cirrus SR22",    getName: function() {        return this.name;    },    fly: function() {        return this.name + ": Flying...";    }};

myAircraft.getName(); //"Cirrus SR22"myAircraft.fly(); //"Cirrus SR22: Flying..."

Page 63: The many facets of code reuse in JavaScript

Prototypal inheritance

var myJet = Object.create(myAircraft);myJet.name = "Boeing 747";myJet.fly = function() {    return this.name + ": Flying a jet...";}

myJet.getName(); //"Boeing 747"myJet.fly(); //"Boeing 747: Flying a jet..."

Page 64: The many facets of code reuse in JavaScript

Weaknesses• Lack of private members - all properties are public• No easy access to super• In the pseudoclassical pattern, forgetting new will breakyour code

Page 65: The many facets of code reuse in JavaScript

Weaknesses• Lack of private members - all properties are public• No easy access to super• In the pseudoclassical pattern, forgetting new will breakyour code

Strengths• Using the prototype is the fastest way to create objects when compared to closures• In practice it will only matter if you’re creating thousands of objects

Page 66: The many facets of code reuse in JavaScript

Functional inheritance

var aircraft = function(spec) {    var that = {};    that.getName = function() {        return spec.name;    };    that.fly = function() {        return spec.name + ": Flying...";    };    return that;};

Page 67: The many facets of code reuse in JavaScript

Functional inheritance

var aircraft = function(spec) {    var that = {};    that.getName = function() {        return spec.name;    };    that.fly = function() {        return spec.name + ": Flying...";    };    return that;};

Members declared here are private

Page 68: The many facets of code reuse in JavaScript

Functional inheritancevar myAircraft = aircraft({ name: "Cirrus SR22" });myAircraft.getName(); //"Cirrus SR22"myAircraft.fly(); //"Cirrus SR22: Flying..."

Page 69: The many facets of code reuse in JavaScript

Functional inheritancevar myAircraft = aircraft({ name: "Cirrus SR22" });myAircraft.getName(); //"Cirrus SR22"myAircraft.fly(); //"Cirrus SR22: Flying..."

myAircraft.that; //undefined

Page 70: The many facets of code reuse in JavaScript

Functional inheritancevar jet = function(spec) {    var that = aircraft(spec);    that.fly = function() {        return spec.name + ": Flying a jet...";    };    return that;};

Page 71: The many facets of code reuse in JavaScript

Functional inheritancevar jet = function(spec) {    var that = aircraft(spec);    that.fly = function() {        return spec.name + ": Flying a jet...";    };    return that;};

that is now an aircraft

Page 72: The many facets of code reuse in JavaScript

Functional inheritancevar jet = function(spec) {    var that = aircraft(spec);    that.fly = function() {        return spec.name + ": Flying a jet...";    };    return that;};

var myJet = jet({ name: "Boeing 747" });    myJet.getName(); //"Boeing 747"myJet.fly(); //"Boeing 747: Flying a jet..."

that is now an aircraft

Page 73: The many facets of code reuse in JavaScript

But I want to reuse my fly function

Page 74: The many facets of code reuse in JavaScript

Implementing super

Object.prototype.super = function(fName) {    var that = this;    var f = that[fName];

    return function() {        return f.apply(that, arguments);    };};

Page 75: The many facets of code reuse in JavaScript

Revisiting jetvar jet = function(spec) {    var that = aircraft(spec),        superFly = that.super("fly");    that.fly = function() {        return superFly() + "a frickin' jet!";    };    return that;};

var myJet = jet({    name: "Boeing 747"});myJet.fly(); //"Boeing 747: Flying...a frickin' jet!"

Page 76: The many facets of code reuse in JavaScript

Weaknesses• Consumes more memory: every object created allocates new function objects as necessary• In practice it will only matter if you’re creating thousands of objects

Functional inheritance

Page 77: The many facets of code reuse in JavaScript

Weaknesses• Consumes more memory: every object created allocates new function objects as necessary• In practice it will only matter if you’re creating thousands of objects

Strengths• It’s conceptually simpler than pseudoclassical inheritance• Provides true private members• Provides a way of working with super (albeit verbose)• Avoids the new workaround since new isn’t used at all

Functional inheritance

Page 78: The many facets of code reuse in JavaScript

Both Prototypal and Functional patterns are powerful

Page 79: The many facets of code reuse in JavaScript

Both Prototypal and Functional patterns are powerful

I’d avoid the pseudoclassical path

Page 80: The many facets of code reuse in JavaScript

But wait! Inheritance isn’t the only way to share behaviour

Page 81: The many facets of code reuse in JavaScript

An alternative to inheritance:Mixins

var utils = {};utils.enumerable = {    reduce: function(acc, f) {        for (var i = 0; i < this.length; i++) {            acc = f(acc, this[i]);        }        return acc;    }};

Page 82: The many facets of code reuse in JavaScript

An alternative to inheritance:Mixins

var utils = {};utils.enumerable = {    reduce: function(acc, f) {        for (var i = 0; i < this.length; i++) {            acc = f(acc, this[i]);        }        return acc;    }};

Sitck it into a module so as to avoid clobbering the global namespace

Page 83: The many facets of code reuse in JavaScript

Mixins - implementing extends

utils.extends = function(dest,source) {    for (var prop in source) {        if (source.hasOwnProperty(prop)) {            dest[prop] = source[prop];        }    }};

Page 84: The many facets of code reuse in JavaScript

Mixins extending Array.prototype

utils.extends(Array.prototype, utils.enumerable);

Page 85: The many facets of code reuse in JavaScript

Mixins extending Array.prototype

utils.extends(Array.prototype, utils.enumerable);

[1,2,3].reduce(0, function(acc,item) {    acc += item;    return acc;}); // 6

Page 86: The many facets of code reuse in JavaScript

Going apeshit

functional

Page 87: The many facets of code reuse in JavaScript

Partial function application

Page 88: The many facets of code reuse in JavaScript

Partial function application

var sum = function(a, b) {    return a + b;};

Page 89: The many facets of code reuse in JavaScript

Partial function application

var sum = function(a, b) {    return a + b;};

var inc = utils.partial(sum, 1);

Page 90: The many facets of code reuse in JavaScript

Partial function application

var sum = function(a, b) {    return a + b;};

var inc = utils.partial(sum, 1);

inc(8); //9

Page 91: The many facets of code reuse in JavaScript

Partial function application

Page 92: The many facets of code reuse in JavaScript

Partial function application

var times = function(a, b) {    return a * b;}

Page 93: The many facets of code reuse in JavaScript

Partial function application

var times = function(a, b) {    return a * b;}

var double = utils.partial(times, 2);

Page 94: The many facets of code reuse in JavaScript

Partial function application

var times = function(a, b) {    return a * b;}

var double = utils.partial(times, 2);

double(10); //20

Page 95: The many facets of code reuse in JavaScript

Partial function application

utils.partial = function(f) {    var sourceArgs = Array.prototype.slice.apply(arguments, [1]);    return function() {        var actualArgs = Array.prototype.concat.apply(sourceArgs, arguments);        return f.apply(null, actualArgs);    };};

Page 96: The many facets of code reuse in JavaScript

Partial function application

utils.partial = function(f) {    var sourceArgs = Array.prototype.slice.apply(arguments, [1]);    return function() {        var actualArgs = Array.prototype.concat.apply(sourceArgs, arguments);        return f.apply(null, actualArgs);    };};

f**k yeah

Page 97: The many facets of code reuse in JavaScript

Function composition:Reversing a string

Page 98: The many facets of code reuse in JavaScript

Function compositionutils.split = function(str) {    return String.prototype.split.apply(str, [""]);};

utils.reverse = function(array) {    return Array.prototype.reverse.apply(array);};

utils.join = function(array) {    return Array.prototype.join.apply(array, [""]);};

Page 99: The many facets of code reuse in JavaScript

Function composition

var reverseStr = utils.comp(utils.split, utils.reverse, utils.join);

Page 100: The many facets of code reuse in JavaScript

Function composition

var reverseStr = utils.comp(utils.split, utils.reverse, utils.join);

reverseStr("leonardo"); //odranoel

Page 101: The many facets of code reuse in JavaScript

Function composition

utils.comp = function() {    var functions = Array.prototype.slice.apply(arguments, [0]);    return function() {        var result = functions.reduce(arguments, function(r, f) {            return [f.apply(null, r)]        });        return result[0];

    };};

Page 102: The many facets of code reuse in JavaScript

Function composition

utils.comp = function() {    var functions = Array.prototype.slice.apply(arguments, [0]);    return function() {        var result = functions.reduce(arguments, function(r, f) {            return [f.apply(null, r)]        });        return result[0];

    };};

f**k yeah

Page 103: The many facets of code reuse in JavaScript

Thankfully implements these - and much more - for us!

Page 104: The many facets of code reuse in JavaScript

Bottom line

Page 105: The many facets of code reuse in JavaScript

Bottom line

• Embrace JavaScript’s true prototypal nature

Page 106: The many facets of code reuse in JavaScript

Bottom line

• Embrace JavaScript’s true prototypal nature• Use mixins, prototypal and functional inheritance to dispense with classes and the new keyword

Page 107: The many facets of code reuse in JavaScript

Bottom line

• Embrace JavaScript’s true prototypal nature• Use mixins, prototypal and functional inheritance to dispense with classes and the new keyword• Use higher order functions, partial application and function composition as elegant alternatives to promote code reuse

Page 108: The many facets of code reuse in JavaScript

Bottom line

• Embrace JavaScript’s true prototypal nature• Use mixins, prototypal and functional inheritance to dispense with classes and the new keyword• Use higher order functions, partial application and function composition as elegant alternatives to promote code reuse• Above all, understand each pattern’s strengths and weaknesses

Page 109: The many facets of code reuse in JavaScript

Thanks!

Questions?

Leonardo Borges@leonardo_borges

http://www.leonardoborges.comhttp://www.thoughtworks.com

Page 110: The many facets of code reuse in JavaScript

References

• JavaScript: the Good Parts (http://amzn.to/zY04ci)• Test-Driven JavaScript Development (http://amzn.to/yHCexS) • Secrets of the JavaScript Ninja (http://bit.ly/wOS4x2)• Closures wiki article (http://bit.ly/xF2OfP)• Underscore.js - functional programming for JS (http://bit.ly/JLkIbm)• Presentation source code (http://bit.ly/HU8kL6)

Leonardo Borges@leonardo_borgeshttp://www.leonardoborges.comhttp://www.thoughtworks.com