JS Code Quality

for Large JS Application



goo.gl/hfwTvi




JS Dude / @mdkhan005

www.thatJSdude.com

(youtube.com/user/khanLearning)

Md Khan

Application Development

Plain simple JS


//salute.js
var mySalute = 'Hello';
						

//app.js
var result = mySalute + ' world!';
						

Common JS


//salute.js
var mySalute = 'Hello';
module.exports = mySalute;
						

//app.js
var mySalute = require('./salute');
var Result = mySalute + 'world!';
						

Export function


//salute.js
module.exports = function(){
  return 'Hello JS';
};
						

//app.js
var salute = require('./salute');
console.log(salute());
						

Expose multiple value


//salute.js
module.exports = {
  foo:'Hello JS',
  bar:'Beer JS'
};
						

//salute.js
module.exports.foo = 'Hello JS';
module.exports.bar = 'Beer JS';
						

//app.js
var salute = require('./salute');
console.log(salute.foo);
console.log(salute.bar);
						

Tree Dependency


//bar.js
var privateBar = 'Bar';
var bar = privateBar + ' is not so far!';
module.exports = bar;
						

//foo.js
var bar = require( './bar' );
exports.foo = function(){
    return 'Hello ' + bar;
}
						

//app.js
var foo = require('./foo');
console.log(foo());
						

How to create module

  • Similar Responsibility
  • Have a top-level scope that is not the global scope
  • Shared code together
  • Think reusability, maintainability, readability
  • Load with dependency order


module example

ES6 module

  • Support synchronous and asynchronous loading
  • Support for cyclic dependencies between modules




es6 modules

ES2015


// library.js
function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}

export function doubleSquare(x) {
  return square(x) * square(x);
}

//or
export { cube, doubleSquare };
						

import { cube, doubleSquare } from 'library';
console.log(cube(4)); // 64
						

es6 export


// export a named variable
export var x = 42;                      

// export a named function
export function foo() {};               

// export the default export
export default 42;                      

// export the default export as a function
export default function foo() {};       

						

//export an export from another module
export { encrypt as en } from 'crypto';

//export all exports from another module 
export * from 'crypto';  
						

import


// import a module without any import bindings
import 'jquery';                        

// import the default export of a module
import $ from 'jquery';                 

// import a named export of a module
import { $ } from 'jquery';             

// import a named export to a different name
import { $ as jQuery } from 'jquery';   
						

import * as myModule from "my-module";

import {foo, bar} from "my-module";


import {reallyReallyLongModuleMemberName as shortName, anotherLongModuleName as short} from "my-module";
						

MDN: es6 import

CommonJS vs ES6


//commonJS
exports.someMethod = function() {

  };

exports.another = {};
						

//es6
export function someMethod() {

 }

export var another = {};

						

react es6
  Scripts Modules
HTML element <script> <script type="module">
Top-level variables global local to module
Value of this at top level window undefined
Executed synchronously asynchronously
Declarative imports (import statement) no yes
Programmatic imports (Promise-based API) yes yes

es6 modules

Module bundler

module bundler

Webpack

  • Split your app into multiple files
  • Load asynchronously on demand
  • Module all static resources
  • Handles style, image, fonts, templates, etc.
  • Create single or multiple chunks
  • Dependencies are resolved during compilation
  • Integrate with build system Grunt/gulp

getting started


 npm install webpack -g
						

webpack app.js bundle.js
						

npm install css-loader style-loader
						


getting started with webpack
webpack advanced

webpack.config.js


module.exports = {
    entry: "./app.js",
    output: {
        path: __dirname,
        filename: "bundle.js"
    },
    module: {
        loaders: [
            { test: /\.css$/, loader: "style!css" }
        ]
    }
};
						

webpack

//for production
webpack -p

//for continuous incremental
webpack --watch  
						

Code Splitting

  • Define split points in code
  • Webpack resolve dependencies, create output files
  • Small Initial download
  • Load chunks on demand during runtime

if (window.location.pathname === '/feed') {
  require.ensure(['./feedCore'], function() { 
    // when this function is called, the module is guaranteed 
    // to be synchronously available.
    require('./feed').show(); 
  });
} else if (window.location.pathname === '/profile') {
  require.ensure(['./profileCore'], function() {
    require('./profile').show();
  });
}
						

code splitting
Common Chunk Plugin

open as much as we needed

Testing

Make things testable

function should do only one thing


function createGreeting(name, location, age) {
    let greeting;
    if (location === 'Mexico') {
        greeting = '!Hola';
    } else {
        greeting = 'Hello';
    }

    greeting += ' ' + name.toUpperCase() + '! ';

    greeting += 'You are ' + age + ' years old.';

    return greeting;
}
						

Write Testable JS

Break it down


function getBeginning(location) {
    if (location === 'Mexico') {
        return '¡Hola';
    } else {
        return 'Hello';
    }
}
						

function getMiddle(name) {
    return ' ' + name.toUpperCase() + '! ';
}

function getEnd(age) {
    return 'You are ' + age + ' years old.';
}

function createGreeting(name, location, age) {
    return getBeginning(location) + getMiddle(name) + getEnd(age);
}
						

Make function Pure

  • Doesn’t depend on or modify variables outside of its scope
  • Produces no side effects
  • Given the same input, will always return the same output

function mouseOnLeftSide(mouseX) {
    return mouseX < window.innerWidth / 2;
}
						




Make JavaScript Pure

Why Pure

  • Self-documenting
  • Know dependency at declaration
  • Avoid changes in global variables
  • Referential transparency

function square(x) {
    return x * x;
}

function squareThemAll(items){
  return items.map(square);
}
						

Static Analysis

  • JSLint
  • JSHint
  • ESLint

Unit Testing

  • Test Framework: Jasmine, mocha
  • Test Runner: Karma, Protractor
  • Automate Browser testing: PhantomJS, Selenium
  • Test Coverage: Istanbul, blanketJS
  • Continuous Integration
  • Continuous Delivery



JS unit testing
Automated Unit test
Angular Testing

Jasmine Unit test


describe("A spec using beforeEach and afterEach", function() {
  var foo = 0;

  beforeEach(function() {
    foo += 1;
  });

  afterEach(function() {
    foo = 0;
  });

  it("is just a function, so it can contain any code", function() {
    expect(foo).toEqual(1);
  });

  it("can have more than one expectation", function() {
    expect(foo).toEqual(1);
    expect(true).toEqual(true);
  });
});
						

Jasmine
Jamine mock ajax

Istanbul code coverage


Coverage Report

Folder Structure

angular 2: directory structure


angular2 style guide

So far...

1. Module

2. Module Bundler

3. Make things testable

Trivia-1



						

Does html support block level links?





Front End Quiz

Trivia -2


My picture
						

Does the HTML above trigger a http request when the page first loads ?


My photo

Does the HTML above trigger a http request when the page first loads?

Trivia-3


4 + 3 + 2 + "1"
						


    
    

						

Does main1.css have to be downloaded and parsed before main2.css can be fetched?



    


    

Paragraph 1

Paragraph 2

Does main2.css have to be downloaded and parsed before Paragraph 1 is rendered on the page?


var foo = function bar() {}; 
alert(typeof bar);
						


var arr = [];
arr[0]  = 'a';
arr[1]  = 'b';
arr.foo = 'c';
alert(arr.length);
						

Coding Style

Boolean flags


myThings.setData({ x: 1 }, true);
						

What is the menaing of true?


myThings.mergeData({ x: 1 });
						

Be Explicit over Implicit


// Support Opera Mini, which returns NaN for undefined minlength
if(isNaN(value)){
  value = undefined;
}
						
jquery source code

// Support Opera Mini, which returns NaN for undefined minlength
var isNaNForUndefined = isNaN(value);

if (isNaNForUndefined) {
  value = undefined;
}
						

naming convention


There are only 2 hard things in front end: fix IE bugs and naming things


var elapsedTimeInDays;
var description;

var god_damn_its_IE;
						

var $d; //elapsed time in days
var dsc; //description

var f_IE;
						

Other will bang their head


//When I wrote this, only God and I understood what I was doing
//Now, God only knows
function chartNumber(value){
  var power = Math.floor(Math.log10(value));
  var fraction = value/Math.pow(10, power);
  var ceilFraction = Math.ceil(fraction *2)/2;
  return ceilFraction * Math.pow(10, power);
}
						
  • Name of the function doesn't tell what the function do
  • Comment the purpose of the function
  • Meaningful variable name


Variable decleration


// bad
let i, len, dragonball,
    items = getItems(),
    goSportsTeam = true;
						

// bad
let i;
const items = getItems();
let dragonball;
const goSportsTeam = true;
let len;
						

// good
const goSportsTeam = true;
const items = getItems();
let dragonball;
let i;
let length;
						

Airbnb JS style

Function

use Function Declaration

  • Are named: easier to identify in call stack
  • Whole body of function is hoisted
  • Don't declare a function in a non-function block (if, while, etc)

// bad
const foo = function () {
};

// good
function foo() {
}
						

arguments

  • Never name a parameter arguments
  • Never use arguments, use rest syntax ...
  • ... is explicit about which arguments
  • rest arguments are a real Array
  • arguments is Array-like object

// bad
function concatenateAll() {
  const args = Array.prototype.slice.call(arguments);
  return args.join('');
}

// good
function concatenateAll(...args) {
  return args.join('');
}
						

parameters

  • Use default Parameter
  • Put default parameters last
  • Never Reassign or mutate parameter
  • 
    // bad
    function f1(obj) {
      obj.key = 1;
    };
    
    // good
    function f2(obj) {
      const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1;
    };
    							

Read this

see this


es6 parameter

Arrow Function


var evens = [2,4,6];

//es5
var odds = evens.map(function(item){
	return item + 1;
})

var pair = evens.map(function(item){
	return {
		even: item,
		odd: item + 1
	}
});
						

//es6
var odds = evens.map(v => v + 1);

var pairs = evens.map(v => ({even: v, odd: v + 1}));
						

Arrow Function

  • Talk what is arrow function
  • creates own version of execution context, this
  • In place of function expression
  • anonymous function
  • 
    // bad
    [1, 2, 3].map(function (x) {
      const y = x + 1;
      return x * y;
    });
    
    // good
    [1, 2, 3].map((x) => {
      const y = x + 1;
      return x * y;
    });
    							

Don't use arrow function


When 'not' to use arrow functions

Cleverness

Things could be clever, but code doesn't have to

Be Generous to New Hire


// Magic. Do not touch.
function magic(x){
  return x >> 0; 
}
						

//be generous
return Math.floor(x);
						

const plus1 = a => a + 1;
const mult2 = a => a * 2;

// little confusing
mult2(plus1(5)); // => 12
						

Don't be clever


// bad
return x === 0 ? 'red' : x === 1 ? 'green' : 'blue';

// ok
if (x === 0) {
    return 'red';
} else if (x === 1) {
    return 'green';
} else {
    return 'blue';
}
						

// good
switch (x) {
    case 0:
        return 'red';
    case 1:
        return 'green';
    default:
        return 'blue';
}
						

Speaking JavaScript

Performance vs Readability


//You are not meant to understand this
const arr = [1, 2, 3, 4];
const len = arr.length;
var i = -1;
var result = [];
while (++i < len) {
  var n = arr[i];
  if (n % 2 > 0) continue;
  result.push(n * n);
}
						

const isEven = n => n % 2 == 0;
const square = n => n * n;

const result = arr.filter(isEven).map(square);
						

JavaScript guideline

Loop


	
// no comments for you
// it was hard to write
// so it should be hard to read
const sum = arr => {
  var sum = 0;
  var i = -1;
  for (;arr[++i];) {
    sum += arr[i];
  }
  return sum;
};

sum([1, 2, 3]); // => 6
						

const sum = arr =>
  arr.reduce((x, y) => x + y);

sum([1, 2, 3]); // => 6
						

Minimize Dependencies


// bad
// I have to find a different job
var _ = require("underscore");
_.compact(["foo", 0]));
_.unique(["foo", "foo"]);
_.union(["foo"], ["bar"], ["foo"]);
						

// good
//thank god, i survived
const compact = arr => arr.filter(el => el);
const unique = arr => [...Set(arr)];
const union = (...arr) => unique([].concat(...arr));

compact(["foo", 0]);
unique(["foo", "foo"]);
union(["foo"], ["bar"], ["foo"]);
						

you might not need Jquery

Document

comment


// 
// Dear maintainer:
// 
// Once you are done trying to 'optimize' this routine,
// and have realized what a terrible mistake that was,
// please increment the following counter as a warning
// to the next guy:
// 
// total_hours_wasted_here = 42
						
  • Explain your intention in comments
  • Warn of consequences in comments
  • Emphasis important points in comments
  • Never leave code commented



code comment

JS-Doc


Pressing tab after /**
						

use JSDoc
sublime-jsdoc
docco

Logging



Writing good code is hard and reproducing client-side errors is even harder

Log JavaScript Error


window.onerror = function(message, source, lineno, colno, error) { 
  var req = new XMLHttpRequest();
  var params = 'msg='' + encodeURIComponent(msg) + 
  			'&url=' + encodeURIComponent(url) +
  			'&line='' + line;
  req.open('POST', '/scripts/logerror.php');
  req.send(params);
}
						

Code Review

Review Checklist


//convert to JS Date oboject from yyyymmdd
function parseDate (dateString){
  var match = dateString.match(/(\d{4})(\d{2})(\d{2})/);
  var formattedDate = match[2] + '/' + match[3] + '/' + match[1];
  return new Date(formattedDate);
}
						
  • What happens when dateString is in a different format?
  • How this deals with timezone?
  • Server and client side time offset
  • Is it generic parsing or specific parsing of date string?


MDN: Reviewer Checklist

Review tools

  • Crucible
  • Code Collaborator
  • Review Board


brad frost code review
Lesson from a code review

security

Security

js xss
Facebook sixth sense

Module Everything

Writing CSS is easy.

Writing maintainable CSS is hard.



Structure large project css
reuse CSS
react css module

CSS Module


//contianer.sass
@import 'stylesheets/config/colors'
.container
  background: $blue
						


//header.sass
@import 'stylesheets/config/colors'
.header
  font-weight: bold
  font-size: 3em
  color: $red
						

//footer.sass
.footer
  font-style: italic
						

css build with webpack

CSS build with Webpack



//webpack-css-example
npm install
npm run build
Take a look at the generated CSS in build/
						

generated CSS


/*app.css*/
html {
  font-family: sans-serif; }
.header {
  font-weight: bold;
  font-size: 3em;
  color: #f00; }
.u-clearfix {
  overflow: hidden; }
.footer {
  font-style: italic; }
.container {
  background: steelblue; }

						

Module Everything else


Webpack practical examples
html loader
webpack image loader

Need more?

List of style guides

CSS resources: to be read

git pre commit hook

Take a Moment

Press B

Five Things

Five Things

  1. Module
  2. Module Bundler
  3. Make things testable
  4. Coding Style
  5. Modularize Everything

Code Quality




(reality)

Code Quality

“ As a developer our job is to promise quality code at the beginning of the project and somehow mess it up in six months.”

Real Development Process

  1. Biggest jerk makes all big decisions
  2. GDD: Google Driven Development
  3. SDD: Stackoverflow Driven Development
  4. Always report progress as “It’s Almost Done”
  5. Don't finish the feature when there is code freeze.
  6. Reduce JSlint warnings: //ignore file at top



development process

Too many Broken builds

measure code quality

Free Tip

Travel Tips

Empty water bottle

Avoid luggage check in


avoid check in fee

One Thing

Use Webpack

Thank you



goo.gl/hfwTvi




JS Dude / @mdkhan005

www.thatJSdude.com

(youtube.com/user/khanLearning)