"Writing Maintainable JavaScript". Jon Bretman, Badoo

2,535 views
2,454 views

Published on

There are lot of tools (CoffeeScript, Typescript, Dart, JSLint / JSHint etc.) that we can use to help us write better JavaScript, and many frameworks (Backbone, Ember, Angular etc.) that can help us structure large applications. But... what if you already have a large code base and are not able to re-write your whole application in a new way? What if your organisation does not want to depend on some open source or third party tool or framework? I am going to talk about some of the key things that most of these tools and frameworks do and how you can apply them to your existing or new project. Topics covered will include type checking, data hiding (public, private, static), inheritance, asynchronous code and performance.

Published in: Technology
0 Comments
3 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
2,535
On SlideShare
0
From Embeds
0
Number of Embeds
1,944
Actions
Shares
0
Downloads
18
Comments
0
Likes
3
Embeds 0
No embeds

No notes for slide

"Writing Maintainable JavaScript". Jon Bretman, Badoo

  1. 1. WRITING MAINTAINABLE JAVASCRIPT Yet Another Conference 2013 @jonbretman jonbretman.co.uk Mobile Web Developer @ Badoo http://techblog.badoo.com @BadooTech 1"
  2. 2. 2"
  3. 3. <!doctype html>! <html>! <head>! <title>My Awesome App</title>! <script src="App.js"></script>! </head>! <body>! </body>! </html>" 3"
  4. 4. 4" What is maintainability?
  5. 5. 5" In engineering, maintainability is the ease with which a product can be maintained in order to... h(p://en.wikipedia.org/wiki/Maintainability"
  6. 6. 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"
  7. 7. 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"
  8. 8. 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"
  9. 9. 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"
  10. 10. 10" It's about making our lives easier
  11. 11. 11" It's about making our work pass the test of time
  12. 12. 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||1<e&e==h&&S>2|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<N|d&&N}function W(){! i="<table>";for(u=18;u<99;document.body.innerHTML=i+=++u%x-9?! "<th width=60 height=60 onclick='I[b="+u+"]>8?W():X(0,0,1)'style='font-size:50px'bgcolor=#"! +(u-B?u*.9&1||9:"d")+"0f0e0>&#"+(I[u]?9808+l[67+I[u]]:160):u++&&"<tr>")B=b}W()" h(p://js1k.com/2010Efirst/demo/750"
  13. 13. 13"
  14. 14. 14" •  Mobile Web Team - 4 developers
  15. 15. 15" •  Mobile Web Team - 4 developers •  JavaScript, jsDoc, JSHint, Closure Compiler, JsTestDriver
  16. 16. 16" •  Mobile Web Team - 4 developers •  JavaScript, jsDoc, JSHint, Closure Compiler, JsTestDriver •  60,000+ lines of JavaScript
  17. 17. 17" •  Mobile Web Team - 4 developers •  JavaScript, jsDoc, JSHint, Closure Compiler, JsTestDriver •  60,000+ lines of JavaScript •  ~500,000 daily active users
  18. 18. 18" •  Mobile Web Team - 4 developers •  JavaScript, jsDoc, JSHint, Closure Compiler, JsTestDriver •  60,000+ lines of JavaScript •  ~500,000 daily active users •  Code maintainability is key!
  19. 19. var topics = [! 'Type Checking',! 'Classes and Inheritance’,! 'Asynchronous Code',! 'Performance'! ];! " 19"
  20. 20. topics.shift();! "Type Checking"! ! 20"
  21. 21. <!doctype html>! <html>! <head>! <title>My Awesome App</title>! <script src="App.js"></script>! </head>! <body>! </body>! </html>" 21"
  22. 22. 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"
  23. 23. 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"
  24. 24. 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"
  25. 25. 25" Exceptions
  26. 26. if (data.value) {! callback(data.value);! }! ! ! TypeError: Cannot read property 'value' of null! ! ! ! ! " 26"
  27. 27. if (data && data.callback) {! var result = data.callback();! }! ! ! TypeError: Property 'callback' of object #<Object> is not a function" " 27"
  28. 28. if (data && data.value) {! var index = data.value.indexOf('something');! }! ! ! TypeError: Object #<Object> has no method ‘indexOf’ ! ! ! ! ! 28"
  29. 29. typeof {};! "object"! ! typeof 'hello';! "string"! ! typeof 5;! "number”! typeof function () {};! "function"! ! typeof undefined;! "undefined"! ! typeof true;! "boolean"! ! 29"
  30. 30. typeof [];! "object"! ! typeof null;! "object"! ! typeof new Date();! "object"! typeof /jsconf/;! "object"! ! typeof document.body;! "object"! ! typeof NaN;! "number"! ! 30"
  31. 31. Object.prototype.toString()! ! " " " 31"
  32. 32. Object.prototype.toString()! ! " " " 32" WHY?
  33. 33. •  If the this value is undefined, return "[object Undefined]".! ! 33"
  34. 34. •  If the this value is undefined, return "[object Undefined]".! •  If the this value is null, return "[object Null]".! ! 34"
  35. 35. •  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"
  36. 36. •  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"
  37. 37. •  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()!
  38. 38. var toString = Object.prototype.toString;! var regex = /[object (.*?)]/;! ! var type = function (o) {! var match = toString.call(o).match(regex);! return match[1].toLowerCase();! };! 38"
  39. 39. 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"
  40. 40. type({});! "object"! ! type('hello');! "string"! ! type(5);! "number”! type(function () {});! "function"! ! type(undefined);! "undefined"! ! type(true);! "boolean"! ! 40"
  41. 41. type([]);! "array"! ! type(null);! "null"! ! type(new Date());! "date"! type(/jsconf/);! "regex"! ! type(document.body);! "htmlbodyelement"! ! type(NaN);! "number"! ! 41"
  42. 42. type([]);! "array"! ! type(null);! "null"! ! type(new Date());! "date"! type(/jsconf/);! "regex"! ! type(document.body);! "htmlbodyelement"! ! type(NaN);! "number"! ! 42" ???
  43. 43. 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"
  44. 44. 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
  45. 45. 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
  46. 46. 46" Now what?
  47. 47. 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"
  48. 48. 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"
  49. 49. 49" How does this help maintainability?
  50. 50. 50" Prevents unexpected breakdowns
  51. 51. 51" Prevents unexpected breakdowns Makes future maintenance easier
  52. 52. topics.shift();! "Classes and Inheritance"! ! 52"
  53. 53. 53" Why classes?
  54. 54. 54" var Controller = {! ! init: function () {! // do some initialization! },! ! loadView: function () {! ! }! ! };! ! // somewhere else in the app! Controller.init();! Controller.loadView();"
  55. 55. 55" var Controller = {! ! init: function () {! // do some initialization! },! ! loadView: function () {! ! }! ! };! ! // somewhere else in the app! Controller.init();! Controller.loadView();" Feels messy
  56. 56. 56" var ChatController = {};! ! for (var key in Controller) {! ChatController[key] = Controller;! }! ! ChatController.loadView = function () {! ! Controller.loadView.apply(this, arguments); // do some additional stuff! ! };! "
  57. 57. 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
  58. 58. 58" What do we want?
  59. 59. class Controller {! !! public Controller () {! // do some initialization! }! !! ! public void loadView () {! ! !! ! }! ! }! ! class ChatController extends Controller {! !! ! public void loadView () {! ! ! super.loadView();! ! ! // do some additional stuff! ! }! !! }" 59"
  60. 60. class Controller {! ! constructor () {! // do some initialization! }! ! loadView () {! ! }! ! }! ! class ChatController extends Controller {! ! loadView () {! super.loadView();! // do some additional stuff! }! ! }" 60"
  61. 61. class Controller! ! constructor: () ->! # do some initialization! ! loadView: () ->! ! ! class ChatController extends Controller! ! loadView: () ->! super! # do some initialization" 61"
  62. 62. 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"
  63. 63. 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
  64. 64. 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
  65. 65. 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"
  66. 66. 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
  67. 67. 67" There is no such thing as magic.
  68. 68. 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"
  69. 69. 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
  70. 70. 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
  71. 71. 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;! };" 71" ECMAScript 5
  72. 72. 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;! };" 72" Add shorthand to super
  73. 73. var controller = new Controller();! var chat = new ChatController();! ! controller instanceof Controller; // true! ! chat instanceof Controller; // true! chat instanceof ChatController; // true" 73"
  74. 74. 74" The rest is about good practice
  75. 75. Use getter and setter methods alert(jon.name);! jon.name = 'John';! ! ! alert(jon.getName());! jon.setName('John');! jon.set('name', 'John');" 75"
  76. 76. Define all properties on the prototype, even if they are null. /**! * The persons age.! * @type {Number}! */! Person.prototype.age = null;" 76"
  77. 77. Mark private methods with a leading or trailing underscore // somethings are best kept private :)! Person.prototype._singInShower = function () {! ! };" 77"
  78. 78. Use static methods / properties Person.prototype.EVENTS = {! WALK: 'WALK',! TALK: 'TALK'! };! ! Person.EVENTS = {! WALK: 'WALK',! TALK: 'TALK'! };" 78"
  79. 79. 79" How does this help maintainability?
  80. 80. 80" Correct defects or their causes
  81. 81. 81" Correct defects or their causes Makes future maintenance easier
  82. 82. topics.shift();! "Asynchronous Code"! ! 82"
  83. 83. 83" The big question...
  84. 84. Callbacks or Promises 84"
  85. 85. Promises •  Requires a library to provide the functionality 85"
  86. 86. 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 86"
  87. 87. 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… 87"
  88. 88. h(p://promisesEaplus.github.io/promisesEspec/" 88"
  89. 89. 89" TL;DR
  90. 90. 90" But that must mean...
  91. 91. Callback Hell 91"
  92. 92. 92" 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));! }!
  93. 93. 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));! }!
  94. 94. 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));! }! Action Error Handling
  95. 95. 95" No.
  96. 96. “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/ 96"
  97. 97. doSomething(function (err, response) {! ! });" 97"
  98. 98. 98" var handler = function (err, response) {! ! };! ! doSomething(handler);"
  99. 99. 99" ! 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();! }!
  100. 100. 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();! }!
  101. 101. 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();! }!
  102. 102. 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();! }! Reusable
  103. 103. Avoid anonymous functions 103"
  104. 104. Avoid anonymous functions 104" Useless stack traces
  105. 105. Avoid anonymous functions 105" Useless stack traces Sign of poor structure
  106. 106. Keep things shallow 106"
  107. 107. Keep things shallow 107" Means you are probably using anonymous functions
  108. 108. Keep things shallow 108" Means you are probably using anonymous functions Everyone will hate you
  109. 109. 109" How does this help maintainability?
  110. 110. 110" Isolate defects or their causes
  111. 111. 111" Isolate defects or their causes Makes future maintenance easier
  112. 112. 112" Isolate defects or their causes Makes future maintenance easier Prevent unexpected breakdowns
  113. 113. topics.shift();! "Performance"! ! 113"
  114. 114. var i = 0;! var thing;! for (; i < things.length; i++) {! thing = things[i];! }" things.forEach(function (thing, i) {! ! });" 114" or…
  115. 115. 115" http://jsperf.com/foreachvsloop 24x faster 13x faster 13x Faster 350,000 operations per second
  116. 116. $('a').on('click', function (e) {! ! });! ! ! ! ! ! $('#container').on('click', 'a', function (e) {! ! });" or… 116"
  117. 117. 117" http://jsperf.com/domevents 21x faster 19x faster 21x Faster Only 1000 operations per second
  118. 118. 118" $('#container').append('<ul></ul>');! for (var i = 0; i < messages.length; i++) {! $('#container')! .find('ul')! .append('<li>' + messages[i].text + '</li>');! }" ! ! ! ! var html = '<ul>';! for (var i = 0; i < messages.length; i++) {! html += '<li>' + messages[i].text + '</li>';! }! html += '</ul>';! $('#container').html(html);! or…
  119. 119. 119" http://jsperf.com/renderinghtml 48x Faster 44x faster 15x faster Less than 200 operations per second!
  120. 120. DOM operations 120"
  121. 121. DOM operations Iteration / function calls 121"
  122. 122. 122" Beware of premature optimizations
  123. 123. 123" Bottlenecks
  124. 124. var cache = {! ! get: function (key) {! return localStorage.getItem(key);! },! ! set: function (key, value) {! localStorage.setItem(key, value);! }! ! };" 124"
  125. 125. var cache = {! ! get: function (key) {! return localStorage.getItem(key);! },! ! set: function (key, value) {! localStorage.setItem(key, value);! }! ! };" 125" Disc IO
  126. 126. 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);! }! };" 126"
  127. 127. 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" Memory
  128. 128. 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" Quicker reading
  129. 129. 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" Saving for later
  130. 130. 130" http://jsperf.com/localstoragevsmemory 3x faster About the same
  131. 131. 131" How does this help maintainability?
  132. 132. 132" Makes future maintenance easier
  133. 133. 133" Makes future maintenance easier Prevent unexpected breakdowns
  134. 134. 134" topics.shift();! undefined"
  135. 135. 135" What is maintainability?
  136. 136. 136" It's about making our lives easier
  137. 137. 137" It's about making our work pass the test of time
  138. 138. Thank you! 138" Yet Another Conference 2013 @jonbretman jonbretman.co.uk Mobile Web Developer @ Badoo http://techblog.badoo.com @BadooTech

×