Front-End Developer Series – HTML, CSS, JS and Performance – Promises



Front-End Developer Series – HTML, CSS, JS and Performance – Promises

0 0


csn-fed-presentation-javascript

Front-End Developer Series - Presentation - Maintainable JavaScript and Best Practices.

On Github pwcberry / csn-fed-presentation-javascript

Front-End Developer Series

HTML, CSS, JS and Performance

A quiet chat about maintenance

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

Donald Knuth

Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live.

— Kyle Richter (@kylerichter) December 15, 2010

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