Front-End Developer Series

HTML, CSS, JS and Performance

Carsales Front-End

A quiet chat about maintenance

Programs are meant to be read by humans, only incidentally by computers to execute

Donald Knuth

Introducing JavaScript

Programming Style

If there is a feature of the language that is sometimes problematic, and if it can be replaced with another feature that is more reliable, then always use the more reliable feature.

Douglas Crockford

// any number that isn't zero evaluates to TRUE
var a = [1, 2, 3];
while (a.length) {
  // do something with array
}


// any string that isn't empty evaluates to TRUE
var s = "This is interesting";
if (s) {
  // while s.length is a postive number,
  // we can just evaluate the string.
}


// any object reference that isn't null or undefined
// evaluates to TRUE
var o = {};
if (o) {
  // 'o' exists, let's do something with it
}


// an example ajax success callback
// receiving (parsed) JSON data
function success(data) {
  // check if data was returned as array
  if (data && data.length) {
    // do something with the array
  } else if (data) {
    // only one object
  }
}


// "zero" (0) evalues to FALSE
var elements = $('#Container > div');
if (!elements.length) {
  // There are no elements in this jQuery object
}


// An empty string ('') evaluates to FALSE
var s = '';
if (!s) {
	// Do something with the empty string
}


// "undefined" evaluates to FALSE
var data;
if (!data) {
  // data is not defined ('undefined' by default)
}


// "null" evaluates to FALSE
function Widget() {
  this.element = null;
}
var w = new Widget();
if (!w.element) {
  // element is null
}


// Because of "false", we have "coalesce"
(function() {
  // CSN is the root namespace
  // If it hasn't already been defined, do so
  // Thanks to the short circuit "or",
  // the left side will evalute to FALSE
  // and assign an empty object to the 
  // global "CSN" and the local variable.

  var CSN = window.CSN || (window.CSN = {});

}());

  
(function($, CSN) {

  // define hidden objects and variables here

  // define functions, classes here that are part of namespaces

  // define jQuery plugins

}(jQuery, (window.CSN || (window.CSN = {}))));
  
  
  
(function(CSN){
  // "CSN" is the root namespace
  // Check if the "Utils" namespace has been defined
  // and then use an alias variable to ease typing

  var utils = CSN.Utils || (CSN.Utils = {});

  // Add public functions to CSN.Utils

  utils.format = function(s) {
    // defines CSN.Utils.format()
  };

}((window.CSN || (window.CSN = {}))));
  
  

// You need to record the index of each element
var element, inputs = document.getElementById('Module').getElementsByTagName('input');

for (var i, j = inputs.length; i < j; i++) {
  element = inputs.eq(i);
  // Retain the value of "i" for each iteration
  // By passing the value into this IIFE
  (function(i) {
    element.on('focus', function() {
      // do something with "i"
      $(this).data('index', i);
    });
  })(i);
}


// You need to record the index of each element
// Even better
$('#Module input').each(function(i, element){
  // Each index is recorded
  $(element).data('index', i);
});


function Widget(element) {
  this.element = element;
}

Widget.prototype.change = function() {};

Widget.prototype.init = function() {
  this.element.on('click', function() {
    // In here, "this" refers to the element
  });
}


function Widget(element) {
  this.element = element;
}

Widget.prototype.change = function() {};

Widget.prototype.init = function() {
  var that = this;
  this.element.on('click', function() {
    // In here, "this" refers to the element
    // But we can use "that" 
    // to refer to the containing object
    that.change();
  });
}


'use strict';
// Using Function.bind()
function Widget(element) {
  this.element = element;
}

Widget.prototype.change = function() {};

Widget.prototype.init = function() {
  var that = this;
  this.element.on('click', function(ev) {
    // In here "this" refers to
    // the containing object
    this.change();
  }.bind(this));
}


// Function currying
JsonpService.postAjax = function (aspect, canPipe, itemCountKey) {
    return function (data, error, msg) {
        var newCount;
        if (!error) {
            newCount = parseInt(data[itemCountKey], 10);
            if (!isNaN(newCount)) {
                this.itemCount = newCount;
            }

            if (canPipe) {
                data = this.pipe("out", data);
            }

            this.notify(aspect, data);

            if (this.itemCount >= this.maximum) {
                this.notify("full", this.itemCount);
            }
        } else {
            this.errorHandler(error, msg);
        }
    };
};

JsonpService.prototype.create = function (data) {
  var handler = JsonpService.postAjax("create", true, "NewCount");
  // wrap in jQuery.proxy to improve lexical scope
  // and pass the handler to jQuery.ajax
};


(function($) {
  'use strict';
  
  // "ToolboxWidget" class is hidden from outside world
  function ToolboxWidget(element) {
    this.element = element;
    // Define other properties
  }
  ToolboxWidget.prototype.init = function() {
    // Initialize state
    // Invoke other setup methods
  };

  $.fn.toolbox = function(options) {
    options = $.extend({}, $.fn.toolbox.defaults, options);

    this.each(function() {
      // The ToolboxWidget handles the state
      // of the object. We're reducing the
      // number of DOM interactions to
      // improve performance.
      var w = new ToolboxWidget($(this));
      w.init();

      // do more with widget

      w.data('widget', w);
    });
  
    return this;
  };

  $.fn.toolbox.defaults = {};

}(jQuery));


<body>
<!-- generated HTML here -->

<!-- scripts at bottom of page for performance -->
<script src="./scripts/toolbox-widget.js"></script>
<!-- define entry point for Toolbox Widget, with configuration -->
<script>
(function($){
$("#Toolbox").toolbox({searchUrl:"/results/{0}"});
}(jQuery));
</script>
</body>


<body>
<!-- generated HTML here -->

<!-- scripts at bottom of page for performance -->
<script src="./scripts/app.js"></script>
<!-- set up, with configuration -->
<script>
(function($){
CSN.App.init({startPage:2});
}(jQuery));
</script>
</body>


function someMethod() {
  return
  {
    name: "Peter"
  }
}


function someMethod() {
  return {
    name: "Peter"
  };
}


if (a > 10)
  doThis();
else
  doThat();


if (a > 10)
  doThis();
else
  doThat();
  doSomethingElse();


if (a > 10) {
  doThis();
  doSomethingElse();
} else {
  doThat();
}


// typeof "ABC" == "string"
// typeof 2134 == "number"
// typeof {} == "object"
// typeof function() {} == "function"
// typeof null == "object"
// typeof [] == "object"
// typeof undefined == "undefined"


var a = "123";
if (a == 123) {
  // TRUE
  // a is "coerced" into a number
}

var b;
if (b == null) {
  // TRUE
  // "undefined" is coerced into being "null"
}


var a = "123";
if (a === 123) {
  // a HAS to be a number for this to be TRUE
}

var b;
if (b === null) {
  // b MUST have null assigned to it to be TRUE
}

// even better:
if (!b) {
  // use FALSY rule
}


(function() {
  'use strict';

  // OK, define module
  // Any variable not defined will throw an error
}());


(function() {
  'use strict';

  var a = [1, 2, 3];
  
  a.forEach(function(item) {
    // do something on each array element
  });

}());


(function() {
  'use strict';

  var person = {
    firstName: 'Peter',
    lastName: 'Berry',
    age: 41,
    lives: 'Brunswick'
  };

  // Object.keys returns an array of properties
  var properties = Object.keys(person);

  // We can then use forEach to enumerate
  // This code will output the value of each property
  properties.forEach(function(p){
    console.log(person[p]);
  });
}());

JS Lint


jQuery("body").on('click', function() {
    a = a + 1
    if (a == 1) alert('Hello World);
})

JS Hint


jQuery("body").on('click', function() {
    a = a + 1
    if (a == 1) alert('Hello World);
})

JSON Parser

	
		{ "name": "Peter","description":"has written some dodge code in the past", "flagged":true, "age":41}
	

JS Beautifier

	
		{ "name": "Peter","description":"has written some dodge code in the past", "flagged":true, "age":41}
	

Code in a manner that another developer, who is not familiar with JavaScript, can comprehend the intent of the code.

Libraries and Frameworks

Promises

Handling events and passing to nested functions is messy - "callback hell"

Promises has a fluent API


// Nested callbacks		
http.get(url.parse("http://example.org/"), function (res) {
    console.log(res.statusCode);  // maybe 302
    http.get(url.parse(res.headers["location"]), function (res) {
        console.log(res.statusCode);  // maybe 200
    });
});


// Promises
httpGet(url.parse("http://example.org/")).then(function (res) {
    console.log(res.statusCode);  // maybe 302
    return httpGet(url.parse(res.headers["location"]));
}).then(function (res) {
    console.log(res.statusCode);  // maybe 200
});

Block Scope


function makeDo(arg) {
  if (arg.isValid) {
    let a = arg.value;
    // do something with a
    // "a" is in block scope
  }	
}

Performance

Hosted by: Alastair Burrowes

Resources

Essential Viewing

Maintainable JavaScript

Nicholas Zakas

Douglas Crockford

JavaScript is cool