Slide 1

Slide 1 text

ASK NOT WHAT JAVASCRIPT CAN DO FOR YOU Embracing the language and writing sensible code @jonbretman jonbretman.co.uk Mobile Web Developer @ Badoo http://techblog.badoo.com @badootechbog 1  

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

4  

Slide 5

Slide 5 text

! ! ! My Awesome App! ! ! ! !   5  

Slide 6

Slide 6 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);! ! });! 6  

Slide 7

Slide 7 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);! ! });! 7  

Slide 8

Slide 8 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 8  

Slide 9

Slide 9 text

if (data.value) {! // do something with value! }! ! ! TypeError: Cannot read property 'value' of null! ! ! ! !       9  

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

Object.prototype.toString()!         14  

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 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.! ! ! 17  

Slide 18

Slide 18 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 "]".! ! 18  

Slide 19

Slide 19 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 "]".! ! Function.prototype.call()! ! ! ! ! ! ! !or! Function.prototype.apply()!   19  

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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

Slide 25

Slide 25 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;! };! 25  

Slide 26

Slide 26 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;! };! 26   Special case for DOM elements

Slide 27

Slide 27 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;! };! 27   Special case for NaN

Slide 28

Slide 28 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);! ! });   28  

Slide 29

Slide 29 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% 29  

Slide 30

Slide 30 text

Check for types where not doing so could cause an exception or make code ambiguous 30  

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

public class Person {! !! !private String name;! ! !public Person (String name) {! ! !this.name = name;! !}! !! !public String getName () {! ! !return this.name;! !}! ! }! ! public class Doctor extends Person {! !! !public Doctor (String name) {! ! !super("Dr." + name);! !}! !! }   32  

Slide 33

Slide 33 text

class Person {! ! name :String = null! ! constructor (name :String) {! this.name = name;! }! ! getName () :String {! return this.name;! }! ! }! ! class Doctor extends Person {! ! constructor (name :String) {! super('Dr. ' + name);! }! ! }   33  

Slide 34

Slide 34 text

class Person! ! name: null! ! constructor: (@name) ->! ! getName: () ->! @name! ! class Doctor extends Person! ! constructor: (name) ->! super('Dr. ' + name)   34  

Slide 35

Slide 35 text

var Doctor, Person,! __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;! };! ! Person = (function() {! Person.prototype.name = null;! ! function Person(name) {! this.name = name;! }! ! Person.prototype.getName = function() {! return this.name;! };! ! return Person;! })();! ! Doctor = (function(_super) {! __extends(Doctor, _super);! ! function Doctor(name) {! Doctor.__super__.constructor.call(this, 'Dr. ' + name);! }! ! return Doctor;! })(Person);   35  

Slide 36

Slide 36 text

var Doctor, Person,! __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;! };! ! Person = (function() {! Person.prototype.name = null;! ! function Person(name) {! this.name = name;! }! ! Person.prototype.getName = function() {! return this.name;! };! ! return Person;! })();! ! Doctor = (function(_super) {! __extends(Doctor, _super);! ! function Doctor(name) {! Doctor.__super__.constructor.call(this, 'Dr. ' + name);! }! ! return Doctor;! })(Person);   Utility method 36  

Slide 37

Slide 37 text

var Doctor, Person,! __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;! };! ! Person = (function() {! Person.prototype.name = null;! ! function Person(name) {! this.name = name;! }! ! Person.prototype.getName = function() {! return this.name;! };! ! return Person;! })();! ! Doctor = (function(_super) {! __extends(Doctor, _super);! ! function Doctor(name) {! Doctor.__super__.constructor.call(this, 'Dr. ' + name);! }! ! return Doctor;! })(Person);   Class Definitions 37  

Slide 38

Slide 38 text

var Person = function (name) {! this.name = name;! };! ! Person.prototype.name = null;! ! Person.prototype.getName = function() {! return this.name;! };! ! var Doctor = function (name) {! Person.call(this, 'Dr. ' + name);! };! ! extends(Doctor, Person);   38  

Slide 39

Slide 39 text

var Person = function (name) {! this.name = name;! };! ! Person.prototype.name = null;! ! Person.prototype.getName = function() {! return this.name;! };! ! var Doctor = function (name) {! Person.call(this, 'Dr. ' + name);! };! ! extends(Doctor, Person);   The magic bit 39  

Slide 40

Slide 40 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;! };   40  

Slide 41

Slide 41 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;! };   Copy static properties / methods 41  

Slide 42

Slide 42 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;! };   Set up prototype chain 42  

Slide 43

Slide 43 text

var jon = new Person('Jon');! var will = new Doctor('Will');! ! jon instanceof Person; // true! jon.getName(); // "Jon"! ! will instanceof Person; // true! will instanceof Doctor; // true! will.getName(); // "Dr. Will"   43  

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

Callbacks or Promises 49  

Slide 50

Slide 50 text

Promises •  Requires a library to provide the functionality 50  

Slide 51

Slide 51 text

Promises •  Requires a library to provide the functionality •  Different implementations •  jQuery Deferred •  RSVP.js •  when.js 51  

Slide 52

Slide 52 text

Promises •  Requires a library to provide the functionality •  Different implementations •  jQuery Deferred •  RSVP.js •  when.js •  Kind of complicated… 52  

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

Callback Hell 54  

Slide 55

Slide 55 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/ 55  

Slide 56

Slide 56 text

56   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 57

Slide 57 text

57   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 58

Slide 58 text

58   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 59

Slide 59 text

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

Slide 60

Slide 60 text

60   ! 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 61

Slide 61 text

Avoid anonymous functions 61  

Slide 62

Slide 62 text

Avoid anonymous functions 62   • Useless  stack  traces    

Slide 63

Slide 63 text

Avoid anonymous functions 63   • Useless  stack  traces   • Sign  of  poor  structure  

Slide 64

Slide 64 text

Keep things shallow 64  

Slide 65

Slide 65 text

Keep things shallow 65   • Means  you  are  probably   using  anonymous   funcGons  

Slide 66

Slide 66 text

Keep things shallow 66   • Means  you  are  probably   using  anonymous   funcGons   • Everyone  will  hate  you  

Slide 67

Slide 67 text

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

Slide 68

Slide 68 text

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

Slide 69

Slide 69 text

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

Slide 70

Slide 70 text

70   $('#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 71

    Slide 71 text

    1.DOM operations 71  

    Slide 72

    Slide 72 text

    1. DOM operations 2. Memory management 72  

    Slide 73

    Slide 73 text

    1. DOM operations 2. Memory management 3. Iteration, function calls 73  

    Slide 74

    Slide 74 text

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

    Slide 75

    Slide 75 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);! }! ! };   75  

    Slide 76

    Slide 76 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);! }! ! };   76   Memory

    Slide 77

    Slide 77 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);! }! ! };   77   Quicker reading

    Slide 78

    Slide 78 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);! }! ! };   78   Saving for later

    Slide 79

    Slide 79 text

    79   topics.shift();! undefined  

    Slide 80

    Slide 80 text

    Thank you! @jonbretman jonbretman.co.uk Mobile Web Developer @ Badoo www.badoo.com   80