Slide 1

Slide 1 text

WRITING MAINTAINABLE JAVASCRIPT Yet Another Conference 2013 @jonbretman jonbretman.co.uk Mobile Web Developer @ Badoo http://techblog.badoo.com @BadooTech http://habrahabr.ru/company/badoo @BadooDev 1  

Slide 2

Slide 2 text

2  

Slide 3

Slide 3 text

! ! ! My Awesome App! ! ! ! !   3  

Slide 4

Slide 4 text

4   What is maintainability?

Slide 5

Slide 5 text

5   In engineering, maintainability is the ease with which a product can be maintained in order to... h(p://en.wikipedia.org/wiki/Maintainability  

Slide 6

Slide 6 text

6   In engineering, maintainability is the ease with which a product can be maintained in order to... isolate defects or their cause h(p://en.wikipedia.org/wiki/Maintainability  

Slide 7

Slide 7 text

7   In engineering, maintainability is the ease with which a product can be maintained in order to... correct defects or their cause h(p://en.wikipedia.org/wiki/Maintainability  

Slide 8

Slide 8 text

8   In engineering, maintainability is the ease with which a product can be maintained in order to... prevent unexpected breakdowns h(p://en.wikipedia.org/wiki/Maintainability  

Slide 9

Slide 9 text

9   In engineering, maintainability is the ease with which a product can be maintained in order to... make future maintenance easier h(p://en.wikipedia.org/wiki/Maintainability  

Slide 10

Slide 10 text

10   It's about making our lives easier

Slide 11

Slide 11 text

11   It's about making our work pass the test of time

Slide 12

Slide 12 text

12   for(B=i=y=u=b=i=5-5,x=10,I=[],l=[];B++<304;I[B-1]=B%x?B/x%x<2|B%x<2?7:B/x&4?! 0:l[i++]="ECDFBDCEAAAAAAAAIIIIIIIIMKLNJLKM@G@TSb~?A6J57IKJT576,+-48HLSUmgukgg " +! "OJNMLK IDHGFE".charCodeAt(y++)-64:7);function X(c,h,e,s){c^=8;for(var o,! S,C,A,R,T,G,d=e&&X(c,0)>1e4,n,N=-1e8,O=20,K=78-h<<9;++O<99;)if((o=I[T=O])&&! (G=o^c)<7){A=G--&2?8:4;C=o-9?l[61+G]:49;do if(!(R=I[T+=l[C]])&&!!G|A<3||! (R+1^c)>9&&G|A>2){if(!(R-2&7))return K;n=G|(c?T>29:T<91)?o:6^c;S=! (R&&l[R&7|32]*2-h-G)+(n-o?110:!G&&(A<2)+1);if(e>h||12|d)! {I[T]=n;I[O]=0;S-=X(c,h+1,e,S-N);if(!(h||e-1|B-O|T-b|S<-1e4))return W(),! c&&setTimeout("X(8,0,2),X(8,0,1)",75);I[O]=o;I[T]=R}if(S>N||!h&S==N&&! Math.random()<.5)if(N=S,e>1)if(h?s-S<0:(B=O,b=T,0))break}while(!R&G>2||(T=O,! (G||A>2|(c?O>78:O<41)&!R)&&++C*--A))}return-K+768"+(I[u]?9808+l[67+I[u]]:160):u++&&"")B=b}W()   h(p://js1k.com/2010-­‐first/demo/750  

Slide 13

Slide 13 text

13  

Slide 14

Slide 14 text

14   •  Team of 4 developers

Slide 15

Slide 15 text

15   •  Team of 4 developers •  JavaScript, jsDoc, JSHint, Closure Compiler, JsTestDriver

Slide 16

Slide 16 text

16   •  Team of 4 developers •  JavaScript, jsDoc, JSHint, Closure Compiler, JsTestDriver •  60,000+ lines of JavaScript

Slide 17

Slide 17 text

17   •  Team of 4 developers •  JavaScript, jsDoc, JSHint, Closure Compiler, JsTestDriver •  60,000+ lines of JavaScript •  ~500,000 daily active users

Slide 18

Slide 18 text

18   •  Team of 4 developers •  JavaScript, jsDoc, JSHint, Closure Compiler, JsTestDriver •  60,000+ lines of JavaScript •  ~500,000 daily active users •  Code maintainability is key!

Slide 19

Slide 19 text

var topics = [! 'Type Checking',! 'Classes and Inheritance’,! 'Asynchronous Code',! 'Performance'! ];!   19  

Slide 20

Slide 20 text

topics.shift();! "Type Checking"!   20  

Slide 21

Slide 21 text

! ! ! My Awesome App! ! ! ! !   21  

Slide 22

Slide 22 text

Api.get('/conversations', function (conversations) {! ! var intros = conversations.map(function (c) {! var name = c.theirName;! var mostRecent = c.messages[0].text.substring(0, 30);! return name + ': ' + mostRecent;! });! ! App.renderMessages(intros);! ! });! 22  

Slide 23

Slide 23 text

Api.get('/conversations', function (conversations) {! ! var intros = conversations.map(function (c) {! var name = c.theirName;! var mostRecent = c.messages[0].text.substring(0, 30);! return name + ': ' + mostRecent;! });! ! App.renderMessages(intros);! ! });! 23  

Slide 24

Slide 24 text

Api.get('/conversations', function (err, conversations) {! ! var intros = conversations.map(function (c) {! var name = c.theirName;! var mostRecent = c.messages[0].text.substring(0, 30);! return name + ': ' + mostRecent;! });! ! App.renderMessages(intros);! ! });! A lot of things have to go right here 24  

Slide 25

Slide 25 text

25   Exceptions

Slide 26

Slide 26 text

if (data.value) {! callback(data.value);! }! ! ! TypeError: Cannot read property 'value' of null! ! ! ! !       26  

Slide 27

Slide 27 text

if (data && data.callback) {! var result = data.callback();! }! ! ! TypeError: Property 'callback' of object # is not a function     27  

Slide 28

Slide 28 text

if (data && data.value) {! var index = data.value.indexOf('something');! }! ! ! TypeError: Object # has no method ‘indexOf’ ! ! ! ! !     28  

Slide 29

Slide 29 text

typeof {};! "object"! ! typeof 'hello';! "string"! ! typeof 5;! "number”! typeof function () {};! "function"! ! typeof undefined;! "undefined"! ! typeof true;! "boolean"! ! 29  

Slide 30

Slide 30 text

typeof [];! "object"! ! typeof null;! "object"! ! typeof new Date();! "object"! typeof /jsconf/;! "object"! ! typeof document.body;! "object"! ! typeof NaN;! "number"! ! 30  

Slide 31

Slide 31 text

Object.prototype.toString()!         31  

Slide 32

Slide 32 text

Object.prototype.toString()!         32   WHY?

Slide 33

Slide 33 text

•  If the this value is undefined, return "[object Undefined]".! ! 33  

Slide 34

Slide 34 text

•  If the this value is undefined, return "[object Undefined]".! •  If the this value is null, return "[object Null]".! ! 34  

Slide 35

Slide 35 text

•  If the this value is undefined, return "[object Undefined]".! •  If the this value is null, return "[object Null]".! •  Let class be the value of the [[Class]] property of this. ! 35  

Slide 36

Slide 36 text

•  If the this value is undefined, return "[object Undefined]".! •  If the this value is null, return "[object Null]".! •  Let class be the value of the [[Class]] property of this. •  Return the String value that is the result of concatenating the three Strings "[object ", class, and "]". ! 36  

Slide 37

Slide 37 text

•  If the this value is undefined, return "[object Undefined]".! •  If the this value is null, return "[object Null]".! •  Let class be the value of the [[Class]] property of this. •  Return the String value that is the result of concatenating the three Strings "[object ", class, and "]". ! 37   Function.prototype.call()! or! Function.prototype.apply()!

Slide 38

Slide 38 text

var toString = Object.prototype.toString;! var regex = /\[object (.*?)\]/;! ! var type = function (o) {! var match = toString.call(o).match(regex);! return match[1].toLowerCase();! };! 38  

Slide 39

Slide 39 text

var toString = Object.prototype.toString;! var regex = /\[object (.*?)\]/;! ! var type = function (o) {! var match = toString.call(o).match(regex);! return match[1].toLowerCase();! };! 39   this === o  

Slide 40

Slide 40 text

type({});! "object"! ! type('hello');! "string"! ! type(5);! "number”! type(function () {});! "function"! ! type(undefined);! "undefined"! ! type(true);! "boolean"! ! 40  

Slide 41

Slide 41 text

type([]);! "array"! ! type(null);! "null"! ! type(new Date());! "date"! type(/jsconf/);! "regex"! ! type(document.body);! "htmlbodyelement"! ! type(NaN);! "number"! ! 41  

Slide 42

Slide 42 text

type([]);! "array"! ! type(null);! "null"! ! type(new Date());! "date"! type(/jsconf/);! "regex"! ! type(document.body);! "htmlbodyelement"! ! type(NaN);! "number"! ! ??? 42  

Slide 43

Slide 43 text

var toString = Object.prototype.toString;! var regex = /\[object (.*?)\]/;! ! var type = function (o) {! ! if (o && o.nodeType === 1) {! return 'element';! }! ! var match = toString.call(o).match(regex);! var _type = match[1].toLowerCase();! ! if (_type === 'number' && isNaN(o)) {! return 'nan';! }! ! return _type;! };! 43  

Slide 44

Slide 44 text

var toString = Object.prototype.toString;! var regex = /\[object (.*?)\]/;! ! var type = function (o) {! ! if (o && o.nodeType === 1) {! return 'element';! }! ! var match = toString.call(o).match(regex);! var _type = match[1].toLowerCase();! ! if (_type === 'number' && isNaN(o)) {! return 'nan';! }! ! return _type;! };! 44   Special case for DOM elements

Slide 45

Slide 45 text

var toString = Object.prototype.toString;! var regex = /\[object (.*?)\]/;! ! var type = function (o) {! ! if (o && o.nodeType === 1) {! return 'element';! }! ! var match = toString.call(o).match(regex);! var _type = match[1].toLowerCase();! ! if (_type === 'number' && isNaN(o)) {! return 'nan';! }! ! return _type;! };! 45   Special case for NaN

Slide 46

Slide 46 text

46   Now what?

Slide 47

Slide 47 text

Api.get('/conversations', function (conversations) {! ! if (type(conversations) !== 'array') {! App.renderMessages([]);! return;! }! ! var intros = conversations.map(function (c) {! ! if (type(c) !== 'object') {! return '';! }! ! var name = type(c.theirName) === 'string' ? c.theirName : '';! var mostRecent = '';! ! if (type(c.messages) === 'array' ||! type(c.messages[0]) === 'object' ||! type(c.messages[0].text) === 'string') {! mostRecent = c.messages[0].text.substring(0, 30);! }! ! return name + ': ' + mostRecent;! });! ! App.renderMessages(intros);! ! });   47  

Slide 48

Slide 48 text

Api.get("/conversations",function(e){var t=e.map(function(e) {var t=e.theirName;var n=e.messages[0].text.substring(0,30);return t+": "+n});App.renderMessages(t)})! ! ! ! ! ! ! ! ! ! Api.get("/conversations",function(e){if(type(e)!=="array") {App.renderMessages([]);return}var t=e.map(function(e) {if(type(e)!=="object"){return""}var t=type(e.theirName)==="string"?e.theirName:"";var n="";if(type(e.messages)==="array"|| type(e.messages[0])==="object"|| type(e.messages[0].text)==="string") {n=e.messages[0].text.substring(0,30)}return t+": "+n});App.renderMessages(t)})   + 137% 48  

Slide 49

Slide 49 text

49   How does this help maintainability?

Slide 50

Slide 50 text

50   Prevents unexpected breakdowns

Slide 51

Slide 51 text

51   Prevents unexpected breakdowns Makes future maintenance easier

Slide 52

Slide 52 text

topics.shift();! "Classes and Inheritance"!   52  

Slide 53

Slide 53 text

53   Why classes?

Slide 54

Slide 54 text

54   var Controller = {! ! init: function () {! // do some initialization! },! ! loadView: function () {! ! }! ! };! ! // somewhere else in the app! Controller.init();! Controller.loadView();  

Slide 55

Slide 55 text

55   var Controller = {! ! init: function () {! // do some initialization! },! ! loadView: function () {! ! }! ! };! ! // somewhere else in the app! Controller.init();! Controller.loadView();   Feels messy

Slide 56

Slide 56 text

56   var ChatController = {};! ! for (var key in Controller) {! ChatController[key] = Controller;! }! ! ChatController.loadView = function () {! ! Controller.loadView.apply(this, arguments); // do some additional stuff! ! };!  

Slide 57

Slide 57 text

57   var ChatController = {};! ! for (var key in Controller) {! ChatController[key] = Controller;! }! ! ChatController.loadView = function () {! ! Controller.loadView.apply(this, arguments); // do some additional stuff! ! };!   Not proper inheritance

Slide 58

Slide 58 text

58   What do we want?

Slide 59

Slide 59 text

class Controller {! !! public Controller () {! // do some initialization! }! !! ! public void loadView () {! ! !! ! }! ! }! ! class ChatController extends Controller {! !! ! public void loadView () {! ! ! super.loadView();! ! ! // do some additional stuff! ! }! !! }   59  

Slide 60

Slide 60 text

class Controller {! ! constructor () {! // do some initialization! }! ! loadView () {! ! }! ! }! ! class ChatController extends Controller {! ! loadView () {! super.loadView();! // do some additional stuff! }! ! }   60  

Slide 61

Slide 61 text

class Controller! ! constructor: () ->! # do some initialization! ! loadView: () ->! ! ! class ChatController extends Controller! ! loadView: () ->! super! # do some initialization   61  

Slide 62

Slide 62 text

var ChatController, Controller, _ref,! __hasProp = {}.hasOwnProperty,! __extends = function(child, parent) {! for (var key in parent) {! if (__hasProp.call(parent, key))! child[key] = parent[key];! }! function ctor() {! this.constructor = child;! }! ctor.prototype = parent.prototype;! child.prototype = new ctor();! child.__super__ = parent.prototype;! return child;! };! ! Controller = (function() {! function Controller() {}! Controller.prototype.loadView = function() {};! return Controller;! })();! ! ChatController = (function(_super) {! __extends(ChatController, _super);! ! function ChatController() {! _ref = ChatController.__super__.constructor.apply(this, arguments);! return _ref;! }! ! ChatController.prototype.loadView = function() {! return ChatController.__super__.loadView.apply(this, arguments);! };! ! return ChatController;! })(Controller);   62  

Slide 63

Slide 63 text

var ChatController, Controller, _ref,! __hasProp = {}.hasOwnProperty,! __extends = function(child, parent) {! for (var key in parent) {! if (__hasProp.call(parent, key))! child[key] = parent[key];! }! function ctor() {! this.constructor = child;! }! ctor.prototype = parent.prototype;! child.prototype = new ctor();! child.__super__ = parent.prototype;! return child;! };! ! Controller = (function() {! function Controller() {}! Controller.prototype.loadView = function() {};! return Controller;! })();! ! ChatController = (function(_super) {! __extends(ChatController, _super);! ! function ChatController() {! _ref = ChatController.__super__.constructor.apply(this, arguments);! return _ref;! }! ! ChatController.prototype.loadView = function() {! return ChatController.__super__.loadView.apply(this, arguments);! };! ! return ChatController;! })(Controller);   63   Utility method

Slide 64

Slide 64 text

var ChatController, Controller, _ref,! __hasProp = {}.hasOwnProperty,! __extends = function(child, parent) {! for (var key in parent) {! if (__hasProp.call(parent, key))! child[key] = parent[key];! }! function ctor() {! this.constructor = child;! }! ctor.prototype = parent.prototype;! child.prototype = new ctor();! child.__super__ = parent.prototype;! return child;! };! ! Controller = (function() {! function Controller() {}! Controller.prototype.loadView = function() {};! return Controller;! })();! ! ChatController = (function(_super) {! __extends(ChatController, _super);! ! function ChatController() {! _ref = ChatController.__super__.constructor.apply(this, arguments);! return _ref;! }! ! ChatController.prototype.loadView = function() {! return ChatController.__super__.loadView.apply(this, arguments);! };! ! return ChatController;! })(Controller);   64   Class Definitions

Slide 65

Slide 65 text

var Controller = function () {! // do some initialization! };! ! Controller.prototype.loadView = function() {! ! };! ! var ChatController = function (name) {! Controller.apply(this, arguments);! };! ! ChatController.prototype.loadView = function () {! ChatController._super.loadView.apply(this, arguments);! // do some additional stuff! ! }! ! extends(ChatController, Controller);   65  

Slide 66

Slide 66 text

var Controller = function () {! // do some initialization! };! ! Controller.prototype.loadView = function() {! ! };! ! var ChatController = function (name) {! Controller.apply(this, arguments);! };! ! ChatController.prototype.loadView = function () {! ChatController._super.loadView.apply(this, arguments);! // do some additional stuff! ! }! ! extends(ChatController, Controller);   66   The magic bit

Slide 67

Slide 67 text

67   There is no such thing as magic.

Slide 68

Slide 68 text

var extends = function(child, parent) {! for (var key in parent) {! if (parent.hasOwnProperty(key)) {! child[key] = parent[key];! }! }! function ctor() { ! this.constructor = child; ! }! ctor.prototype = parent.prototype;! child.prototype = new ctor();! child._super = parent.prototype;! return child;! };   68  

Slide 69

Slide 69 text

var extends = function(child, parent) {! for (var key in parent) {! if (parent.hasOwnProperty(key)) {! child[key] = parent[key];! }! }! function ctor() { ! this.constructor = child; ! }! ctor.prototype = parent.prototype;! child.prototype = new ctor();! child._super = parent.prototype;! return child;! };   69   Copy static properties / methods

Slide 70

Slide 70 text

var extends = function(child, parent) {! for (var key in parent) {! if (parent.hasOwnProperty(key)) {! child[key] = parent[key];! }! }! function ctor() { ! this.constructor = child; ! }! ctor.prototype = parent.prototype;! child.prototype = new ctor();! child._super = parent.prototype;! return child;! };   70   Set up prototype chain

Slide 71

Slide 71 text

var extends = function(child, parent) {! for (var key in parent) {! if (parent.hasOwnProperty(key)) {! child[key] = parent[key];! }! }! function ctor() { ! this.constructor = child; ! }! ctor.prototype = parent.prototype;! child.prototype = new ctor();! child._super = parent.prototype;! return child;! };   71   Set up prototype chain (kind of a hack)

Slide 72

Slide 72 text

var extends = function(child, parent) {! for (var key in parent) {! if (parent.hasOwnProperty(key)) {! child[key] = parent[key];! }! }! ! ! ctor.prototype = Object.create(parent.prototype);! ! ! child._super = parent.prototype;! return child;! };   72   ECMAScript 5

Slide 73

Slide 73 text

var extends = function(child, parent) {! for (var key in parent) {! if (parent.hasOwnProperty(key)) {! child[key] = parent[key];! }! }! function ctor() { ! this.constructor = child; ! }! ctor.prototype = parent.prototype;! child.prototype = new ctor();! child._super = parent.prototype;! return child;! };   73   Add shorthand to super

Slide 74

Slide 74 text

var controller = new Controller();! var chat = new ChatController();! ! controller instanceof Controller; // true! ! chat instanceof Controller; // true! chat instanceof ChatController; // true   74  

Slide 75

Slide 75 text

75   The rest is about good practice

Slide 76

Slide 76 text

Use getter and setter methods alert(jon.name);! jon.name = 'John';! ! ! alert(jon.getName());! jon.setName('John');! jon.set('name', 'John');   76  

Slide 77

Slide 77 text

Define all properties on the prototype, even if they are null. /**! * The persons age.! * @type {Number}! */! Person.prototype.age = null;   77  

Slide 78

Slide 78 text

Mark private methods with a leading or trailing underscore // somethings are best kept private :)! Person.prototype._singInShower = function () {! ! };   78  

Slide 79

Slide 79 text

Use static methods / properties when appropriate Person.prototype.EVENTS = {! WALK: 'WALK',! TALK: 'TALK'! };! ! Person.EVENTS = {! WALK: 'WALK',! TALK: 'TALK'! };   79  

Slide 80

Slide 80 text

80   How does this help maintainability?

Slide 81

Slide 81 text

81   Correct defects or their causes

Slide 82

Slide 82 text

82   Correct defects or their causes Makes future maintenance easier

Slide 83

Slide 83 text

topics.shift();! "Asynchronous Code"!   83  

Slide 84

Slide 84 text

84   The big question...

Slide 85

Slide 85 text

Callbacks or Promises 85  

Slide 86

Slide 86 text

Promises •  Requires a library to provide the functionality 86  

Slide 87

Slide 87 text

Promises •  Requires a library to provide the functionality •  Different implementations •  jQuery Deferred api.jquery.com/category/deferred-object/ •  rsvp.js github.com/tildeio/rsvp.js •  when.js github.com/cujojs/when •  promise.js github.com/then/promise 87  

Slide 88

Slide 88 text

Promises •  Requires a library to provide the functionality •  Different implementations •  jQuery Deferred api.jquery.com/category/deferred-object/ •  rsvp.js github.com/tildeio/rsvp.js •  when.js github.com/cujojs/when •  promise.js github.com/then/promise •  Kind of complicated… 88  

Slide 89

Slide 89 text

h(p://promises-­‐aplus.github.io/promises-­‐spec/   89  

Slide 90

Slide 90 text

90   TL;DR

Slide 91

Slide 91 text

91   But that must mean...

Slide 92

Slide 92 text

Callback Hell 92  

Slide 93

Slide 93 text

93   load: function () {! ! Api.get('/profile/own', _.bind(function (ownProfile) {! ! this.ownProfile = ownProfile;! ! Api.get('/profile/' + id, _.bind(function (theirProfile) {! ! this.theirProfile = theirProfile;! ! Api.get('/chatMessages', _.bind(function (messages) {! ! this.messages = messages;! this.render();! ! }, this), _.bind(function (err) {! this.onError();! }, this));! }, this), _.bind(function (err) {! this.onError();! }, this));! }, this), _.bind(function (err) {! this.onError();! }, this));! }! !

Slide 94

Slide 94 text

94   load: function () {! ! Api.get('/profile/own', _.bind(function (ownProfile) {! ! this.ownProfile = ownProfile;! ! Api.get('/profile/' + id, _.bind(function (theirProfile) {! ! this.theirProfile = theirProfile;! ! Api.get('/chatMessages', _.bind(function (messages) {! ! this.messages = messages;! this.render();! ! }, this), _.bind(function (err) {! this.onError();! }, this));! }, this), _.bind(function (err) {! this.onError();! }, this));! }, this), _.bind(function (err) {! this.onError();! }, this));! }! !

Slide 95

Slide 95 text

95   load: function () {! ! Api.get('/profile/own', _.bind(function (ownProfile) {! ! this.ownProfile = ownProfile;! ! Api.get('/profile/' + id, _.bind(function (theirProfile) {! ! this.theirProfile = theirProfile;! ! Api.get('/chatMessages', _.bind(function (messages) {! ! this.messages = messages;! this.render();! ! }, this), _.bind(function (err) {! this.onError();! }, this));! }, this), _.bind(function (err) {! this.onError();! }, this));! }, this), _.bind(function (err) {! this.onError();! }, this));! }! ! Action Error Handling

Slide 96

Slide 96 text

96   No.

Slide 97

Slide 97 text

“I’ve come to the conclusion that callback hell is a design choice and not an inherent flaw in the concept of asynchronous function and callback” http://blog.caplin.com/2013/03/13/callback-hell-is-a-design-choice/ 97  

Slide 98

Slide 98 text

doSomething(function (err, response) {! ! });   98  

Slide 99

Slide 99 text

99   var handler = function (err, response) {! ! };! ! doSomething(handler);  

Slide 100

Slide 100 text

100   ! load: function (id) {! this.id = id;! Api.get('/profile/own', this.onOwnProfile);! },! ! onOwnProfile: function (err, ownProfile) {! if (err) return this.onError();! this.ownProfile = ownProfile;! Api.get('/profile/' + this.id, this.onTheirProfile);! },! ! onTheirProfile: function (err, theirProfile) {! if (err) return this.onError();! this.theirProfile = theirProfile;! Api.get('/chatMessages', this.onMessages);! },! ! onMessages: function (err, messages) {! if (err) return this.onError();! this.messages = messages;! this.render();! }!

Slide 101

Slide 101 text

101   ! load: function (id) {! this.id = id;! Api.get('/profile/own', this.onOwnProfile);! },! ! onOwnProfile: function (err, ownProfile) {! if (err) return this.onError();! this.ownProfile = ownProfile;! Api.get('/profile/' + this.id, this.onTheirProfile);! },! ! onTheirProfile: function (err, theirProfile) {! if (err) return this.onError();! this.theirProfile = theirProfile;! Api.get('/chatMessages', this.onMessages);! },! ! onMessages: function (err, messages) {! if (err) return this.onError();! this.messages = messages;! this.render();! }!

Slide 102

Slide 102 text

102   ! load: function (id) {! this.id = id;! Api.get('/profile/own', this.onOwnProfile);! },! ! onOwnProfile: function (err, ownProfile) {! if (err) return this.onError();! this.ownProfile = ownProfile;! Api.get('/profile/' + this.id, this.onTheirProfile);! },! ! onTheirProfile: function (err, theirProfile) {! if (err) return this.onError();! this.theirProfile = theirProfile;! Api.get('/chatMessages', this.onMessages);! },! ! onMessages: function (err, messages) {! if (err) return this.onError();! this.messages = messages;! this.render();! }!

Slide 103

Slide 103 text

103   ! load: function (id) {! this.id = id;! Api.get('/profile/own', this.onOwnProfile);! },! ! onOwnProfile: function (err, ownProfile) {! if (err) return this.onError();! this.ownProfile = ownProfile;! Api.get('/profile/' + this.id, this.onTheirProfile);! },! ! onTheirProfile: function (err, theirProfile) {! if (err) return this.onError();! this.theirProfile = theirProfile;! Api.get('/chatMessages', this.onMessages);! },! ! onMessages: function (err, messages) {! if (err) return this.onError();! this.messages = messages;! this.render();! }! Reusable

Slide 104

Slide 104 text

Avoid anonymous functions 104  

Slide 105

Slide 105 text

Avoid anonymous functions 105   Useless stack traces

Slide 106

Slide 106 text

Avoid anonymous functions 106   Useless stack traces Sign of poor structure

Slide 107

Slide 107 text

Keep things shallow 107  

Slide 108

Slide 108 text

Keep things shallow 108   Means you are probably using anonymous functions

Slide 109

Slide 109 text

Keep things shallow 109   Means you are probably using anonymous functions Everyone will hate you

Slide 110

Slide 110 text

110   How does this help maintainability?

Slide 111

Slide 111 text

111   Isolate defects or their causes

Slide 112

Slide 112 text

112   Isolate defects or their causes Makes future maintenance easier

Slide 113

Slide 113 text

113   Isolate defects or their causes Makes future maintenance easier Prevent unexpected breakdowns

Slide 114

Slide 114 text

topics.shift();! "Performance"!   114  

Slide 115

Slide 115 text

var i = 0;! var thing;! for (; i < things.length; i++) {! thing = things[i];! }   things.forEach(function (thing, i) {! ! });   115   or…

Slide 116

Slide 116 text

116   http://jsperf.com/foreachvsloop 13x Faster 13.5x faster 24x faster

Slide 117

Slide 117 text

$('a').on('click', function (e) {! ! });! ! ! ! ! ! ! ! ! $('#container').on('click', 'a', function (e) {! ! });   or… 117  

Slide 118

Slide 118 text

118   http://jsperf.com/domevents 21x Faster 21x faster 19x faster

Slide 119

Slide 119 text

119   $('#container').append('
    ');! for (var i = 0; i < messages.length; i++) {! $('#container')! .find('ul')! .append('
  • ' + messages[i].text + '
  • ');! }   ! ! ! ! ! var html = '
      ';! for (var i = 0; i < messages.length; i++) {! html += '
    • ' + messages[i].text + '
    • ';! }! html += '
    ';! $('#container').html(html);! or…

    Slide 120

    Slide 120 text

    120   http://jsperf.com/renderinghtml 48x Faster 44x faster 15x faster

    Slide 121

    Slide 121 text

    DOM operations 121  

    Slide 122

    Slide 122 text

    DOM operations Iteration / function calls 122  

    Slide 123

    Slide 123 text

    123   Beware of premature optimizations

    Slide 124

    Slide 124 text

    124   Bottlenecks

    Slide 125

    Slide 125 text

    var cache = {! ! get: function (key) {! return localStorage.getItem(key);! },! ! set: function (key, value) {! localStorage.setItem(key, value);! }! ! };   125  

    Slide 126

    Slide 126 text

    var cache = {! ! get: function (key) {! return localStorage.getItem(key);! },! ! set: function (key, value) {! localStorage.setItem(key, value);! }! ! };   126   Disc IO

    Slide 127

    Slide 127 text

    var cache = {! ! data_: {},! ! get: function (key) {! ! if (this.data_.hasOwnProperty(key)) {! return this.data_[key];! }! ! var value = localStorage.getItem(key);! ! if (value !== null) {! this.data_[key] = value;! return value;! }! ! return null;! },! ! set: function (key, value) {! this.data_[key] = value;! localStorage.setItem(key, value);! }! ! };   127  

    Slide 128

    Slide 128 text

    var cache = {! ! data_: {},! ! get: function (key) {! ! if (this.data_.hasOwnProperty(key)) {! return this.data_[key];! }! ! var value = localStorage.getItem(key);! ! if (value !== null) {! this.data_[key] = value;! return value;! }! ! return null;! },! ! set: function (key, value) {! this.data_[key] = value;! localStorage.setItem(key, value);! }! ! };   128   Memory

    Slide 129

    Slide 129 text

    var cache = {! ! data_: {},! ! get: function (key) {! ! if (this.data_.hasOwnProperty(key)) {! return this.data_[key];! }! ! var value = localStorage.getItem(key);! ! if (value !== null) {! this.data_[key] = value;! return value;! }! ! return null;! },! ! set: function (key, value) {! this.data_[key] = value;! localStorage.setItem(key, value);! }! ! };   129   Quicker reading

    Slide 130

    Slide 130 text

    var cache = {! ! data_: {},! ! get: function (key) {! ! if (this.data_.hasOwnProperty(key)) {! return this.data_[key];! }! ! var value = localStorage.getItem(key);! ! if (value !== null) {! this.data_[key] = value;! return value;! }! ! return null;! },! ! set: function (key, value) {! this.data_[key] = value;! localStorage.setItem(key, value);! }! ! };   130   Saving for later

    Slide 131

    Slide 131 text

    131   http://jsperf.com/localstoragevsmemory 3x faster Not much in it

    Slide 132

    Slide 132 text

    132   How does this help maintainability?

    Slide 133

    Slide 133 text

    133   Makes future maintenance easier

    Slide 134

    Slide 134 text

    134   Makes future maintenance easier Prevent unexpected breakdowns

    Slide 135

    Slide 135 text

    135   topics.shift();! undefined  

    Slide 136

    Slide 136 text

    136   What is maintainability?

    Slide 137

    Slide 137 text

    137   It's about making our lives easier

    Slide 138

    Slide 138 text

    138   It's about making our work pass the test of time

    Slide 139

    Slide 139 text

    Thank you! 139   Yet Another Conference 2013 @jonbretman jonbretman.co.uk Mobile Web Developer @ Badoo http://techblog.badoo.com @BadooTech http://habrahabr.ru/company/badoo @BadooDev