Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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. 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.
  2. Prototypes ‣ JavaScript’s Agent of Re-use ‣ Flexible but Awkward

    ‣ new <constructor> syntax encourages classical model ‣ Prototype chains are single rooted ‣ Inheritance syntax is gnarly
  3. 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/
  4. 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/
  5. 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
  6. //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 } });
  7. //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 } });
  8. //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 } });
  9. 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)
  10. 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
  11. //A property copy mixin var withWalking = { walk: function()

    { console.log('walking'); }, turn: function(direction) { console.log('turning', direction); }, stopWalking: function() { console.log('stopped walking'); } };
  12. 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/
  13. 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
  14. 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
  15. 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/
  16. function Crocodile(name, gender) { this.name = name; this.gender = gender;

    }; Crocodile.prototype.stalkTourists = function() { //.. }; withWalking.call(Crocodile.prototype); withSwimming.call(Crocodile.prototype);
  17. 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.
  18. 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"
  19. 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); }); }
  20. 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.
  21. 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);
  22. 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)); } }
  23. 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); } }
  24. 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);
  25. 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