$30 off During Our Annual Pro Sale. View Details »

How we Learned to Stop Worrying and Love JavaScript

How we Learned to Stop Worrying and Love JavaScript

Presented at Fluent Conference, San Francisco May 31st 2012

Angus Croll

May 31, 2012
Tweet

More Decks by Angus Croll

Other Decks in Technology

Transcript

  1. How We Learned To Stop Worrying
    And Love JavaScript
    @danwrong @angustweets

    View Slide

  2. We’ve been busy...

    View Slide

  3. View Slide

  4. Rethinking twitter.com
    ‣ Use the right tool for the job. Don’t JavaScript all
    the things.
    ‣ Take advantage of the browser and of the nature
    of JavaScript rather than fighting it.

    View Slide

  5. View Slide

  6. Reuse patterns that make
    use of JavaScript rather
    than fight it

    View Slide

  7. The hardest part...

    View Slide

  8. The hardest part...

    View Slide

  9. The hardest part...

    View Slide

  10. So, talking animals it is...
    meow!!

    View Slide

  11. Prototypes
    ‣ JavaScript’s Agent of Re-use
    ‣ Flexible but Awkward
    ‣ new syntax encourages
    classical model
    ‣ Prototype chains are single rooted
    ‣ Inheritance syntax is gnarly

    View Slide

  12. var Animal = function(gender, says) {
    this.gender = gender;
    this.says = says;
    };
    var WalkingAnimal = function(gender, says) {
    Animal.call(this, gender, says);
    }
    WalkingAnimal.prototype = new Animal();
    // http://jsfiddle.net/96BSq/

    View Slide

  13. var Animal = function(gender, says) {
    this.gender = gender;
    this.says = says;
    };
    var WalkingAnimal = function(gender, says) {
    Animal.call(this, gender, says);
    }
    WalkingAnimal.prototype = new Animal();
    // http://jsfiddle.net/96BSq/

    View Slide

  14. Class syntax
    ‣ JS native class syntax scheduled for ES6
    ‣ class, extend and super are reserved words
    ‣ Library implementation varies widely and
    abstraction is often leaky (‘const’, ‘static’)
    ‣ In particular, the relationship between this and
    super is difficult

    View Slide

  15. //Prototype.js
    var SwimmingAnimal = Class.create(Animal, {
    type: 'SwimmingAnimal',
    speak: function($super) {
    return $super() + ", glug";
    }
    });
    //Dustin Diaz's "klass"
    var Alien = SuperHuman.extend({
    beem: function() {
    this.supr();
    // beem into space
    }
    });

    View Slide

  16. //Prototype.js
    var SwimmingAnimal = Class.create(Animal, {
    type: 'SwimmingAnimal',
    speak: function($super) {
    return $super() + ", glug";
    }
    });
    //Dustin Diaz's "klass"
    var Alien = SuperHuman.extend({
    beem: function() {
    this.supr();
    // beem into space
    }
    });

    View Slide

  17. //Prototype.js
    var SwimmingAnimal = Class.create(Animal, {
    type: 'SwimmingAnimal',
    speak: function($super) {
    return $super() + ", glug";
    }
    });
    //Dustin Diaz's "klass"
    var Alien = SuperHuman.extend({
    beem: function() {
    this.supr();
    // beem into space
    }
    });

    View Slide

  18. Class hierarchies

    View Slide

  19. Class hierarchies
    WTF? I walk
    too!

    View Slide

  20. Class hierarchies
    Duck?
    WTF? I walk
    too!

    View Slide

  21. Class hierarchies
    Duck?
    WTF? I walk
    too!

    View Slide

  22. Classical inheritance
    ‣ Requires up-front knowledge of general case
    ‣ Single rooted hierarchy
    ‣ Types are often too generalized to map to real life
    coding problems (some animals swim AND walk)

    View Slide

  23. Beyond classification
    ‣ 19th century British philosophers Whewell and
    Jevons argued that there are no objectively
    “right” classifications
    ‣ “In general, when working with prototypes, one
    typically chooses not to categorize but to exploit
    alikeness.”
    Antero Taivalsaari (Nokia Research Center) - Journal of Object Oriented
    Programming Nov/Dec 1997

    View Slide

  24. Mixins
    ‣ JavaScript has first class functions
    ‣ Any property can be assigned to any object

    View Slide

  25. //A property copy mixin
    var withWalking = {
    walk: function() {
    console.log('walking');
    },
    turn: function(direction) {
    console.log('turning', direction);
    },
    stopWalking: function() {
    console.log('stopped walking');
    }
    };

    View Slide

  26. var extend = function(destination, source) {
    for (var k in source) {
    if (source.hasOwnProperty(k)) {
    destination[k] = source[k];
    }
    }
    return destination;
    };
    extend(Crocodile.prototype, withWalking);
    extend(Crocodile.prototype, withSwimming);
    //http://jsfiddle.net/29tW5/

    View Slide

  27. Property copy mixins
    ‣ No hierarchical constraints
    ‣ Functionality is grouped by what it does not
    who it belongs to
    ‣ BUT...
    ‣ Mixin has no reference to target properties
    ‣ Properties can only clobber
    ‣ Target object requires intimate knowledge of
    mixin properties

    View Slide

  28. Functional mixins
    ‣ Mixins are functions that assign properties to
    the ‘this’ object.
    ‣ Mixins are directly invoked in the context of the
    target object by means of call/apply

    View Slide

  29. var withWalking = function() {
    this.walk = function() {
    console.log('walking');
    };
    this.turn = function(direction) {
    console.log('turning', direction);
    };
    this.stopWalking = function() {
    console.log('stopped walking');
    };
    };
    //http://jsfiddle.net/uyZZL/13/

    View Slide

  30. function Crocodile(name, gender) {
    this.name = name;
    this.gender = gender;
    };
    Crocodile.prototype.stalkTourists = function() {
    //..
    };
    withWalking.call(Crocodile.prototype);
    withSwimming.call(Crocodile.prototype);

    View Slide

  31. Advice: before, after and around
    ‣ A mixin that adds, underscore.js style, before(),
    after() and around() to an object.
    ‣ Other mixins can use this to augment existing
    methods.

    View Slide

  32. withAdvice.call(Crocodile.prototype);
    function withFlu() {
    this.before(“walk”, function() {
    console.log('sniff');
    })
    }
    var sickCrocodile = new Crocodile();
    withFlu.call(sickCrocodile);
    sickCrocodile.walk();
    //"sniff, pad, pad, pad, pad"

    View Slide

  33. function withCharacterCounter() {
    this.updateCounter = function() {
    //...
    };
    //add advice after core initialize method
    this.after('initialize', function() {
    this.$counter = this.select('counter');
    this.on('uiTextUpdated', this.updateCounter);
    });
    }

    View Slide

  34. Functional mixins
    ‣ Mixins as verbs instead of nouns.
    ‣ Mixins are functions. We can take advantage
    of closure scope, arguments and context.
    ‣ A mixin can be applied to any object type:
    prototype, instance, whatever.
    ‣ Advice allows functional mixins to augment
    existing functions, not clobber them.
    ‣ Works with the language, simple to
    understand, no surprises. Debuggable.

    View Slide

  35. Putting it to work...

    View Slide

  36. function Storage() {
    this.initialize.call(this, arguments);
    }
    function baseStorage() {
    this.initialize = function(namespace) { this.namespace = namespace }
    this.encode = function(item) { return JSON.stringify(item) }
    this.decode = function(item) { return JSON.parse(item) }
    if (window.localStorage) {
    localStorageEngine.call(this);
    return;
    }
    if (document.documentElement.addBehavior) {
    userDataEngine.call(this);
    return;
    }
    memoryStorageEngine.call(this);
    }
    withAdvice.call(Storage.prototype);
    baseStorage.call(Storage.prototype);

    View Slide

  37. function localStorageEngine() {
    this.getItem = function(key) {
    return this.decode(localStorage.getItem(this.namespace + key));
    }
    this.setItem = function(key, val) {
    return localStorage.setItem(this.namespace + key, this.encode(val));
    }
    }

    View Slide

  38. function memoryStorageEngine() {
    var store = {};
    this.after('initialize', function() {
    this.store = store[this.namespace] = store[this.namespace] || {};
    });
    this.getItem = function(key) {
    return this.decode(this.store[this.namespace + key]);
    }
    this.setItem = function(key, val) {
    return this.store[this.namespace + key] = this.encode(val);
    }
    }

    View Slide

  39. function withEncryption() {
    this.after('initialize', function(namespace, secret) {
    this.secret = secret;
    });
    this.around('decode', function(decode, val) {
    return decode(aes.dec(val, this.secret));
    })
    this.around('encode', function(encode, val) {
    return aes.enc(encode(val), this.secret);
    });
    }
    var encryptedStorage = new Storage('secretStuff');
    withEncryption.call(encryptedStorage);

    View Slide

  40. Overview
    ‣ Functional mixins and advice/AOP are the shit
    ‣ Make use of JavaScript’s biggest strength -
    functions
    ‣ Extremely simple
    ‣ Endless flexibility
    ‣ Drop your class implementations and use this
    stuff now

    View Slide

  41. Questions?

    View Slide

  42. @jointheflock
    Visit the booth and chat to us
    twitter.com/jobs
    Happy hour tonight
    Beer.js!

    View Slide