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

Learning to Fly — Twitter Flight and Mixins

Learning to Fly — Twitter Flight and Mixins

Presented at BrazilJS, Porto Alegre, August 22nd 2013

Angus Croll

August 23, 2013
Tweet

More Decks by Angus Croll

Other Decks in Technology

Transcript

  1. the most difficult part of JS inheritance... And on the

    6th day, God created an abundance of Talking Animals, that they may be used in JavaScript inheritance examples. Thursday, August 22, 13
  2. Inheritance classical composition mixins Functions Object.create Constructor.prototype function passing /

    callbacks Objects call/apply functional mixins (Some) JavaScript Re-use Patterns Thursday, August 22, 13
  3. Inheritance classical composition mixins Functions Object.create Constructor.prototype function passing /

    callbacks Objects call/apply functional mixins (Some) JavaScript Re-use Patterns Thursday, August 22, 13
  4. ES 6 is giving us class. Some libraries have their

    own versions. Thursday, August 22, 13
  5. implementation varies widely and abstraction is often leaky (const, static)

    class, extend and super are reserved words in particular, the relationship between this and super is difficult... Thursday, August 22, 13
  6. //Prototype.js var SwimmingAnimal = Class.create(Animal, { type: 'SwimmingAnimal', speak: function($super)

    { return $super() + ", glug"; } }); //https://github.com/ded/klass var Alien = SuperHuman.extend({ speak: function() { this.supr(); //speak like a SuperHuman } }); Thursday, August 22, 13
  7. Animal Walking Animal Swimming Animal Flying Animal Cat Elephant Crocodile

    Whale Eagle Bat Egg-laying Animal Migrating Animal Thursday, August 22, 13
  8. Crocodile Animal Walking Animal Swimming Animal Flying Animal Cat Elephant

    Whale Eagle Bat Egg-laying Animal Migrating Animal Thursday, August 22, 13
  9. Crocodile Animal Walking Animal Swimming Animal Flying Animal Cat Elephant

    Whale Eagle Bat Egg-laying Animal Migrating Animal WTF? I walk too!! Thursday, August 22, 13
  10. Animal Walking Animal Swimming Animal Flying Animal Cat Elephant Crocodile

    Whale Eagle Bat Egg-laying Animal Migrating Animal Thursday, August 22, 13
  11. Duck? Animal Walking Animal Swimming Animal Flying Animal Cat Elephant

    Crocodile Whale Eagle Bat Egg-laying Animal Migrating Animal Thursday, August 22, 13
  12. Duck? Animal Cat Elephant Crocodile Whale Eagle Bat Walking Animal

    Swimming Animal Flying Animal Egg-laying Animal Migrating Animal Thursday, August 22, 13
  13. Classical inheritance Requires up-front knowledge of general case Single rooted

    hierarchy We’re really bad at classification Thursday, August 22, 13
  14. 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(); Thursday, August 22, 13
  15. 19th century British philosophers Whewell and Jevons argued that there

    are no objectively “right” classifications Thursday, August 22, 13
  16. “In general, when working with prototypes, one typically chooses not

    to categorize but to exploit alikeness.” –Antero Taivalsaari (Nokia Research Center) Thursday, August 22, 13
  17. var animalProto = { speak: function() { console.log(this.says); } }

    var walkingAnimalProto = Object.create(animalProto); // then augment walkingAnimalProto here... Thursday, August 22, 13
  18. var animalProto = { speak: function() { console.log(this.says); } }

    var walkingAnimalProto = Object.create( animalProto, { walk: { value: function() { console.log('walking'); } } //... } ); Thursday, August 22, 13
  19. The sad thing is that we even care about inheritance

    hierarchies... Thursday, August 22, 13
  20. The sad thing is that we even care about inheritance

    hierarchies... ...because in JavaScript they’re quite unnecessary... Thursday, August 22, 13
  21. //A property copy mixin var withWalking = { walk: function()

    { console.log('walking'); }, turn: function(direction) { console.log('turning', direction); }, stopWalking: function() { console.log('stopped walking'); } }; Thursday, August 22, 13
  22. 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); Thursday, August 22, 13
  23. No hierarchical constraints Functionality is grouped by what it does

    not who it belongs to Define special cases first; common code later Thursday, August 22, 13
  24. var withWalking = function() { this.walk = function() { console.log('walking');

    }; this.turn = function(direction) { console.log('turning', direction); }; this.stopWalking = function() { console.log('stopped walking'); }; }; Thursday, August 22, 13
  25. function Crocodile(name, gender) { this.name = name; this.gender = gender;

    } Crocodile.prototype.stalkTourists = function() { //.. }; withWalking.call(Crocodile.prototype); withSwimming.call(Crocodile.prototype); Thursday, August 22, 13
  26. function withSelect() { this.defaultAttrs({ selectedIds: [] }); this.selectItem = function($item,

    append) { $item.addClass(this.attr.selectedClass); this.attr.selectedIds.push($item.attr('id')); this.trigger('uiSelectionChanged', {selIds: this.attr.selectedIds}); }; this.unselectItem = function($item) { $item.removeClass(this.attr.selectedClass); this.attr.selectedIds.splice(getIdIndex($item), 1); this.trigger('uiSelectionChanged', {selIds: this.attr.selectedIds}); }; } Thursday, August 22, 13
  27. advice.js is a mixin it lets you add custom code

    before(), after() or around() an existing function. Thursday, August 22, 13
  28. all flight mixins get advice.js for free so mixins can

    augment functions, not clobber them Thursday, August 22, 13
  29. 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" Thursday, August 22, 13
  30. function withSelect() { this.defaultAttrs({ selectedIds: [] }); this.toggleItemSelect = function(ev,

    data) { var $item = $(data.el), append; if ($item.hasClass(this.attr.selectedClass)) { this.unselectItem($item); } else { this.selectItem($item, isMultiSelect(ev)); } }; this.selectItem = function($item, append) {/*..*/}; this.unselectItem = function($item) {/*..*/}; this.after('initialize', function() { this.on(this.select('itemSelector'), 'click', this.toggleItemSelect); }); } Thursday, August 22, 13
  31. Mixins as verbs instead of nouns. Mixins are functions. We

    can take advantage of closure scope and arguments. A mixin can be applied to any object type: prototype, instance, whatever. Advice allows functional mixins to augment existing functions, not clobber them. Thursday, August 22, 13