JavaScript Fundamentals

agenda

  • Introduction
  • Functional JavaScript
  • JavaScript Object Programming
  • Async JavaScript
  • ECMAScript 6 is coming
  • JavaScript Testing
  • Resources guide

introduction

  • JavaScript history
  • Why JavaScript?

1995 - 1997

  • Created by Brendan Eich for Netscape Navigator ...
                 in only ten days!
  • Mocha ⇒ LiveScript ⇒ JavaScript ⇒ ECMAScript
  • Influenced by:
    • C/Java ⇒ Sintaxis
    • Scheme ⇒ FP
    • Self ⇒ Prototypal inheritance
  • Adopted by Microsoft with the name JS for IE 3.0 ...
                 first browser wars!
  • Standarized by ECMA International.

history

1998 - 2002

  • W3C: DOM Level 1 and DOM Level 2
  • Microsoft created XMLHttpRequest
  • Microsoft won the war with IE 6

history

2003 - 2008

  • Mozilla Firefox was born
  • AJAX acronym is coined
  • Google becomes more than just a search engine: GMail, Maps
  • First JavaScript libraries: Prototype, Scriptaculous, etc
  • JQuery: now DOM is easy

history

2009 -

  • Google Chrome was born
  • ECMAScript 5 was approved
  • It began the era of mobile applications (SPA, mobile first)
  • HTML5 & CSS3
  • MV* frameworks move to the client: Angular, Backbone, etc.
  • Server side JavaScript: Node.js
  • JavaScript grows up: The Good parts, testing frameworks, etc

history

  • JavaScript is a misunderstood language
    • The name suggests that it should be similar to Java, but it is a different beast
    • The language was created too quickly and has errors that many programmers do not care to avoid
    • Still exists the idea that serious programming is done on the server and many programmers do not bother to learn JavaScript seriously
  • But in JavaScript there are great ideas too
    • It supports multiple paradigms: imperative or functional
    • It is very flexible and expressive
    • ​There have been significant improvements in performance
    • The language is improving with the latest versions
    • ​Whenever there are better books, tools, libraries, etc

Why JavaScript?

  • It is easy to learn
  • It is easy to write, execute, test and debug
  • It is the most widely used language
  • On the client has beaten all its rivals:
    • VBScript
    • ActionScript (Flash)
    • Applets (Java)
    • Will mobile development platforms be the next victims?
  • On the server is becoming a major player (Node.js)
  • Web applications can be built using only JavaScript:
    • Front-end development
    • Back-end development
    • Server services (Web servers, message queuing services, authentication services, etc)
    • JSON for client-server communication
    • Even database structures (BSON) and query language (MongoDB, CouchDB)
  • Isomorphic JavaScript: The Future of Web Apps?

Why JavaScript?

functional JavaScript

  • It is a controversial term.
  • Some key points:
    • It is a declarative programming paradigm, which means programming is done with expressions
    • The output value of a function depends only on the arguments that are input to the function
    • Has its roots in lambda calculus, a formal system developed in the 1930s to investigate computability

What is FP?

  • The central principle of OOP is that data and operations on it are closely coupled
  • The central tenet of FP is that data is only loosely coupled to functions
  • FP is about expressions (immutable state) / OOP is about methods (mutable state)
  • Whereas the object-oriented approach tends to break problems into groupings of nouns or objects, a functional approach breaks the same problem into groupings of verbs or functions
  • OOP = complex structures with specific manipulation functions
  • FP = simple structures with generic manipulation functions
  • Most OOP-specific design patterns are pretty much irrelevant in functional languages

OOP versus FP

  • Declarative style
  • Modularity
  • Maintainability
  • Easier to debug
  • Easier to test
  • Code reuse
  • Conciseness
  • Clarity
  • Easy concurrency

 

More information (other resource)

 

Why FP matters?

  • Some pure functional languages:
    • Haskell
    • Miranda
  • Some impure functional languages:
    • F#
    • Erlang
    • JavaScript
    • Lisp
    • Clojure
    • Scala
    • Python
    • Ruby
    • C#
    • Java (since version 8)

FP languages

  • First-class functions
  • High-order functions
  • Pure functions ⇒ no side-effects ⇒ immutability
  • Recursion ⇒ No state ⇒ no variables or loops ⇒ Tail call optimization
  • Lazy evaluation
  • Type inference
  • Lexical closures
  • Pattern matching
  • Partial application
  • Continuations

Characteristics of FP 

Bash FP

 $ cat /etc/passwd | cut -f1 -d: |  xargs -I{} sh -c "echo {} | grep -i -o '[aeiou]' | sort | uniq | wc -l" | sort -n | tail -1

How many different vowels has the username that has more different vowels?

in JavaScript without FP

function getMaxVowels() {
  var fs = require('fs');
  var contents = fs.readFileSync('/etc/passwd', 'utf8');
  var userPattern = /^[^:]+/gm;
  var userNames = contents.match(userPattern);
  var maxVowels = 0;
  for (var i = 0; i < userNames.length; i++) {
    var userName = userNames[i].toLowerCase();
    var countVowels = 0;
    var vowels = ['a', 'e', 'i', 'o', 'u'];
    for (var j = 0; j < vowels.length; j++) {
      if (userName.indexOf(vowels[j]) !== -1) {
        countVowels++;
      }
    }
    if (maxVowels < countVowels) {
      maxVowels = countVowels;
    }
  }

  return maxVowels;
}

in JavaScript with FP

function readFile(fileName) {
  var fs = require('fs');
  return fs.readFileSync(fileName, 'utf8');
}

function getUserNames() {
  var contents = readFile('/etc/passwd');
  var userNamePattern = /^(\w|[-])+:/gm;
  return contents.match(userNamePattern).map(function(value) {
    return value.replace(/:$/, '');
  });  
}

function getVowels(string) {
  return string.split('').filter(isVowel);
}

function isVowel(char) {
  return /[aeoiuAEIOU]/.test(char[0]);
}

function uniq(array) {
  return array.reduce(function(result, value) {
    if (result.indexOf(value) === -1) {
      result.push(value);
    }
    return result;
  }, []);
}

function compareNumbers(numberA, numberB) {
  return numberA - numberB;
}

function last(array) {
  return array[array.length - 1];
}

function count(array) {
  return array.length;
}

function maxVowels(array) {
  var vowelsCount = array.map(getVowels).map(uniq).map(count).sort(compareNumbers);
  return last(vowelsCount);
}

maxVowels(getUserNames());

Basic Functions: Passing and returning

parameters

function greet() {
  return 'Hi!';
}

function greet(name) {
  return 'Hi ' + name + '!';
}

function greet(greeting, name) {
  return greeting + ' ' + name + '!';
}

function greet() {
  return arguments[0] + ' ' + arguments[1] + '!';
}

Fisrt-class functions

//Function declaration
function greet() {
  return 'Hi!';
}

typeof greet === 'function'; //true

var greet2 = greet;

greet2 === greet; //true

//Function expression
var greet3 = function() {
  return 'Hi!';
}

greet2 === greet3; //false

greet2() === greet3(); //true

High-order functions

//functions as parameters
function map(array, fn) {
  var i;
  var result = [];
  for (i = 0; i < array.length; i++) {
    result.push(fn(array[i]));
  }
  return result;
}

function double(x) {
  return x * 2;
}

map([1, 2, 3], double); //[2, 4, 6];
//returned functions
function greet(greeting) {
  return function(name) {
    return greeting + ' ' + name + '!';
  };
}

var sayHello = greet('Hi');

sayHello('Peter'); //"Hi Peter!"
sayHello('Anna'); //"Hi Anna!"

Variable Scope:

Global Scope

var firstName = 'Peter';

//global scope <- bad
'firstName' in window; //true

firstName; //"Peter"
window.firstName; //"Peter"


function greet() {
  return 'Hi!';
}

//global scope <- bad
'greet' in window; //true

greet(); //"Hi!"
window.greet(); //"Hi!"

Variable Scope:

Missing var

function greet() {
  //missing var
  greeting = 'Hi!';
}

'greeting' in window; //false

greet();
'greeting' in window; //true

Variable Scope:

use strict

function greet() {
  "use strict";
  greeting = 'Hi!';
}

'greeting' in window; //false

try {
  greet();
} catch(e) {
  console.log('Error: ' + e);
}

'greeting' in window; //false

Variable Scope:

function scope

//JavaScript has function scope
function greet(firstName) {
  var greeting = 'Hi';
  //parametrs and inner variables are visible inside the function
  console.log(greeting + ' ' + firstName + '!');
}

//Here greeting and firstName are not accesible
console.log(firstName); //error firstName is not declared

greeting = 'Bye'; //Create a global variable
"greeting" in window; //true
var greeting = 'Bye';

//Inner variables hide outer variables
function greet() {
  var greeting = 'Hi';
  console.log(greeting); //"Hi"
}

greet(); //"Hi"

console.log(greeting); //"Bye"

Variable Scope:

block scope

//javascript has no block scope
function sum(from, to) {
  var result = 0;
  for (var i = from ; i <= to; i++) {
    result += i;
  }
  
  //console.log(i) -> to + 1 -> i variable is accessible outside the loop
  return result;
}
for (var i = 0; i < 10; i++) {
  ....
}

for (var i = 0; i < 10; i++) { //bad: No complaints but it is a variable redeclaration
  ....
}

Variable hoisting

function greet() {
  console.log(greeting); //undefined -> Declarations are hoisted
  var greeting = 'Hi';
  console.log(greeting); //Hi
}
function greet() {
  //Internally this is what is happening
  var greeting;
  console.log(greeting);
  greeting = 'Hi';
  console.log(greeting); //Hi
}

Function hoisting

function greet(isHello) {
  if (isHello) {
    function g(name) {
      return 'Hi ' + name + '!';
    }
  } else {
    function g(name) { //functions declarations are hoisted too -> Bad: redefinition
      return 'Bye ' + name + '!';
    }
  }
  
  return g;
}

greet(true)('Peter'); //"Bye Peter!" <- unexpected?
//Fixed
function greet(isHello) {
  var g;
  if (isHello) {
    g = function(name) {
      return 'Hi ' + name + '!';
    };
  } else {
    g = function(name) {
      return 'Bye ' + name + '!';
    };
  }  
  return g;
}
//Better
function greet(isHello) {
  if (isHello) {
    return function(name) {
      return 'Hi ' + name + '!';
    };
  } else {
    return function(name) {
      return 'Bye ' + name + '!';
    };
  }
}

Closures(i)

function greet(greeting) {
  return function(firstName) {
    //inner functions can access outer variables -> Closure
    return greeting + ' ' + firstName + '!';
  };
  
  //outer functions can not access inner variables -> Variables have function scope
}

var sayHello = greet('Hello');

sayHello('Peter'); //"Hello Peter" -> Variables survive the execution of the function.
//External variables are accessible even when the function has returned.

Closures(ii)

function greet() {
  var i = 1;
  for(; i <= 10; i++) {
    setTimeout(function() {
      console.log('Hi ' + i);
    }, i * 1000);
  }
}

greet(); //Hi 11 Hi 11 Hi 11 Hi 11 ... 
//why? Closures access the current value of the variables ->
//Access is by reference even in primitive types
//Solution 2: Using IIFE
function greet() {
  var i = 1;
  for(; i <= 10; i++) {
    (function(i) {
      setTimeout(function() {
        console.log('Hi ' + i);
      }, i * 1000);
    }(i));
  }
}

greet(); //Hi 1 Hi 2 Hi 3 Hi 4 ...
//Why does this work? Now i inner variable is a copy of the outer i variable ->
//Primitive variables are passed by value
//Solution 1: Extracting the internal function of the loop
function show(msg) {
  return function() {
    console.log(msg);
  };
}

function greet() {
  var i = 1;
  for(; i <= 10; i++) {
    setTimeout(show('Hi ' + i), i * 1000);
  }
}

greet(); //Hi 1 Hi 2 Hi 3 Hi 4 ...
//Why does this work? show(msg) is executed synchronously ->
//i has the correct value ->
//setTimeout executes the inner function returned by show(msg)

FP in practice

function multiply(x, y) {
  return x * y;
}

multiply(2, 5); //10

//code reuse
function double(x) {
  return multiply(2, x);
}

double(5); //10
//another cool way -> Partial application
var double = multiply.bind(null, 2);

double(5); //10

FP in practice

//Private static variables
var count = (function() {
  var counter = 0;
  
  return function() {
    return ++counter;
  };
}());

count(); //1
count(); //2
count(); //3

FP in practice

function double(x) {
  return 2 * x;
}

function addThree(x) {
  return 3 + x;
}

//composition
double(addThree(5)); //16
//Generic composition: compose two functions with one argument
function compose(f, g) {
  return function(x) {
    return f(g(x));
  };
}
      
var addThreeThenDouble = compose(double, addThree);
addThreeThenDouble(5); //16

FP in practice

function add(x, y) {
  return x + y;
}

function multiply(x, y) {
  return x * y;
}

//Composite is great but difficult to read in JavaScript
multiply(add(multiply(2, 4), 5), 3); //39
//chainable functions
function chain(fns) {
  var _fns = {};
  var result;
  fns.forEach(function(fn) {
    _fns[fn.name] = function() {
      var args = Array.prototype.slice.call(arguments);
      if (result !== undefined) {
        args = [result].concat(args);
      }
      result = fn.apply(null, args);
      return _fns;
    };
  });
  
  _fns.value = function() {
    var _result = result;
    result = undefined;
    return _result;
  };
  
  return _fns;
}


var c = chain([add, multiply]);

//Fluent style
c.multiply(2, 4).add(5).multiply(3).value(); //39

FP in practice

//Lazy chainable version
function chain(fns) {
  var _fns = {};
  var _chain = [];
  fns.forEach(function(fn) {
    _fns[fn.name] = function() {
      var args = Array.prototype.slice.call(arguments);
      _chain.push(function() {        
        return fn.apply(null, Array.prototype.slice.call(arguments).concat(args));
      });
      return _fns;
    };
  });
  
  _fns.execute = function() {
    if (_chain.length === 0) {
      return;
    }
    var result = _chain.slice(1).reduce(function(result, fn) {
      return fn(result);
    }, _chain[0]());
    
    _chain = [];
    return result;
  };
  
  return _fns;
}

FP in practice

//classical way
function double(arr) {
  var i;
  var result = [];
  for (i= 0; i < arr.length; i++) {
    result.push(arr[i] * 2);
  }
  return result;
}

double([1, 2, 3, 4]); //[2, 4, 6, 8];

function isEven(arr) {
  var i;
  var result = [];
  for (i= 0; i < arr.length; i++) {
    result.push(arr[i] % 2 === 0);
  }
  return result;
}

//Bad code reuse -> same structure, different function -> Break the single responsibility principle
isEven([1, 2, 3, 4]); //[false, true, false, true]
function double(x) {
  return 2 * x;
}

function isEven(x) {
  return x % 2 === 0;
}

//creates a new array with the results of calling a provided function on every element in the array
function map(arr, fn) {
  var i;
  var result = [];
  for (i= 0; i < arr.length; i++) {
    result.push(fn(arr[i]));
  }
  return result;
}

map([1, 2, 3, 4], double); //[2, 4, 6, 8]
map([1, 2, 3, 4], isEven); //[false, true, false, true]

//native JavaScript map
[1, 2, 3, 4].map(isEven); //[false, true, false, true]

FP in practice

//Intrusive and no reusable solution
function sum(arr) {
  var result = 0;
  var i;
  for (i = 0; i < arr.length; i++) {
    result += arr[i];
  }
  return result;
}

sum([1, 2, 3, 4]); //10
function sum(x, y) {
  return x + y;
}

function reduce(arr, fn, initValue){
  var i = 0;
  var result = initValue !== undefined ? initValue : arr[i++];
  
  for(; i < arr.length; i++){
     result = fn(result, arr[i]);
 }  
  return result;
}

reduce([1, 2, 3, 4], sum, 0); //10

[1, 2, 3, 4].reduce(sum, 0); //10

FP in practice

//Bad -> intrusive
function evens(arr) {
  var i;
  var result = [];
  for (i= 0; i < arr.length; i++) {
    if (arr[i] % 2 === 0) {
      result.push(arr[i]); 
    }
  }
  return result;
}

evens([1, 2, 3, 4]); //[2, 4]
function isEven(x) {
  return x % 2 === 0;
}

function filter(fn, arr) {
  var i;
  var result = [];
  for (i= 0; i < arr.length; i++) {
    if (fn(arr[i])) {
      result.push(arr[i]); 
    }
  }
  return result;
}

filter(isEven, [1, 2, 3, 4]); //[2, 4]

var evens = filter.bind(null, isEven);

evens([1, 2, 3, 4]); //[2, 4]

[1, 2, 3, 4].filter(isEven); //[2, 4]

FP in practice

//Why use loops at all?
function filter(fn, arr) {
  var result = [];
  arr.forEach(function(value) {
    if (fn(value)) {
      result.push(value); 
    }
  });
  return result;
}
//forEach does not return a value -> This is not very FP approach
function filter(fn, arr) {
  return arr.reduce(function(result, value) {
    return fn(value) ? result.concat(value) : result;
  }, []);
}

FP in practice

//Classical way
function diagonalSum(matrix) {
  var i;
  var sum = 0;
  for (i = 0; i < matrix.length; i++) {
    sum += matrix[i][i];
  }
  return sum;
}

diagonalSum([[1, 2, 3], [4, 5, 6], [7, 8, 9]]); // 1 + 5 + 9 = 15
function zipWith(fn, array1, array2) {
  var result = [];
  var i = 0;
  var size = Math.min(array1.length, array2.length);
  while (i < size) {
    result.push(fn(array1[i], array2[i]));
    i++;
  }
  return result;
}

function getArrayValue(arr, index) {
  return arr[index];
}

//other way: Object.keys(arr)
function indexes(arr) {
  return arr.map(function(value, index) {
    return index;
  });
}
function sum(x, y) {
  return x + y;
}

function diagonalSum(matrix) {
  return zipWith(getArrayValue, matrix, indexes(matrix)).
    reduce(sum);
}

FP in practice

//Classical way
function reverse(arr) {
  var result = [];
  var i = arr.length - 1;
  for (; i >= 0; i--) {
    result.push(arr[i]);
  }
  return result;
}

reverse([1, 2, 3, 4]); //[4, 3, 2, 1]
//Using reduce function
function reverse(arr) {
  return arr.reduce(function(result, value) {
    return [value].concat(result);
  }, []);
}
//Recursive way
function reverse(arr) {
  return arr.length === 0 ? arr : reverse(arr.slice(1)).concat(arr[0]);
}
//Reverse native function
[1, 2, 3, 4].reverse(); //Warning: Array.prototype.reverse produce a state mutation

Exercise

arr.reduce(callback[, initialValue]);

where callback receives: callback(previousValue, currentValue, index, array);

Implement the diagonalSum() function using arr.reduce() method

 

Help: This is the declaration of reduce() method:

You can see a solution here

Exercise

function head(arr) {
  return arr[0];
}

function tail(arr) {
  return arr.slice(1);
}

function last(arr) {
  return arr[arr.length - 1];
}

function first(arr) {
  return arr.slice(0, arr.length - 1);
}

Implement a pure functional version of the zipWith() function.

 

Help: You can use some of these helper functions:

You can see a solution here

Exercise

function map(arr, fn) {
  var i;
  var result = [];
  for (i= 0; i < arr.length; i++) {
    result.push(fn(arr[i]));
  }
  return result;
}

Implement map() function using reduce() function

 

As a remainder this is our actual implementation of the map() function:

You can see a solution here

Exercise

function reduce(arr, fn, initValue){
  var i = 0;
  var result = initValue !== undefined ? initValue : arr[i++];
  
  for(; i < arr.length; i++){
     result = fn(result, arr[i]);
 }  
  return result;
}

Implement a pure functional version of the reduce() function

 

As a remainder this is our actual implementation of the reduce() function:

Note: For a cleaner implementation, suppose that initValue param is always passed.

You can see a solution here

Exercise

frequency(['Peter', 'Anna', 'Rose', 'Peter', 'Peter', 'Anna']); //[["Anna", 2], ["Peter", 3], ["Rose", 1]]
frequency([1, 10, 12, 2, 1, 10, 2, 2]); //[[1, 2], [2, 3], [10, 2], [12, 1]]

Implement the frequency(arr, options) function.

The function takes an array and calculates the frequency of each value of the array sorted naturally. For example:

Optionally, the function can take sorting and grouping criterias. See CodeWars kata

Exercise (hard)

//SELECT * FROM numbers
var numbers = [1, 2, 3, 4, 5, 6];
query().select().from(numbers).where(isEven).execute(); //[2, 4, 6]

function isEven(number) {
    return number % 2 === 0;
}

If you have completed the previous exercise, this is the continuation.  Resolve Functional SQL kata:

JavaScript OP

JavaScript Objects

//objects are maps of (key, value) pairs
var obj = {
  x: 1,
  y: 2
};

//keys are always strings
var obj = {
  1: 1000,  //The key is "1" not 1. Non string keys are converted to strings
  2: -200
};

//quotes are optional
var obj = {
  "1": 1000,
  "2": -200
};

//values can be of any type
var obj = {
  foo: 1000,
  bar: 'Peter',
  baz: function() {},
  zot: [1, 2, 3],
  foobar: /^a/
};

Getting single values

var obj = {
  x: 1,
  y: 2
};

//You can use dot notation...
obj.x; //1

//...or array notation
obj["x"]; //1

//array notation is useful with expressions
var key = 'x';
obj[key]; //1

Getting multiple values

var obj = {
  x: 1,
  y: 2
};

//You can use for..in loop...
for (var key in obj) {
  obj[key];
}

//...but FP style is preferred
Object.keys(obj).forEach(function(key) {
  obj[key];
});

//Important: key iteration order is not guaranteed.

Dynamic objects

var obj = {
  x: 1,
  y: 2
};

"x" in obj; //true
"z" in obj; //false

//keys can be added...
obj.z = 23;
"z" in obj; //true

//...or removed
delete obj.z;
"z" in obj; //false

//Common error: This do not remove the key
obj.x = undefined;
"x" in obj; //true

//Rather, it assigns the undefined value
obj.x === undefined; //true

Expressions in keys

//You can not define a key in this way
var key = "x" + "y"
var obj = {
  key : 3 //the key is "key" and not "xy";
};

//..neither this way is valid
var obj = {
  "x" + "y": 3 //expressions are not allowed here
};

//But you can do this
var obj = {};
obj["x" + "y"] = 3;

//Attention
var obj = {};
var arr = [1, 2];

obj[arr] = 4;
"1,2" in obj; //true

var obj2 = {};

obj[obj2] = 3;
"[object Object]" in obj; //true

//Remember: keys are converted to strings

Arrays

//Arrays are a special type of objects
typeof [] === 'object'; //true

//Arrays can have holes
var arr = [1,,,2];

//indexes are keys
"0" in arr; //true

//holes are not keys
"1" in arr; //false

//holes are equal to undefined
arr[1] === undefined; //true

//but careful: It is not the same be undefined than it be equal to undefined
arr[1] = undefined; //this is not a hole
arr[1] === undefined; //true
"1" in arr; //true <- this is the different

//Arrays are dynamic
var arr = [1, 2, 3];
arr[500] = 4; //from index 3 to index 499 there are holes

//The length property is not what it seems
arr.length; //501

Arrays (ii)

//You should not use for..in loop to iterate through arrays...
//because the iteration order is not guaranteed
//There are plenty of methods to iterate over arrays:
//forEach, map, reduce, filter, some, every, ...

//Careful: delete comand make holes, it does not remove the element
var arr = [1, 2, 3];
delete arr[3];
arr.length; //3

//To remove properly, you should use splice method instead
arr.splice(2, 1);
arr.length; //2

Object vs. primitive types

//As in Java, JavaScript difference between objects and primitive types.

//Primitive types are compared/passed by value
"Peter" === "Peter"; //true
1 === 1; //true
false === false; //true

//Objects types are compared/passed by reference
{} === {}; //false
[1] === [1]; //false
function(){} = function(){}; //false

//You can use primitive as objects (I do not think this is helpful at all)
new String('Peter') === new String('Peter'); //false

//You can extract the primitive of an object
new String('Peter').valueOf() === new String('Peter').valueOf(); //true

//Or convert to a primitive
String(1) === String(1); //true

Object vs. primitive types (ii)

//Primitive types have no methods but are converted to objects when used as such
"Peter".toUpperCase(); //PETER

//The wrapper object is deleted when the method returns
//Therefore the primitive may not have keys
//as they are added to the discarted wrapper object
var person = "Peter";
person.age = 3;
person.age; //undefined
"age" in person; //error <- person is primitive

//However, this can be done with objects
var person = new String('Peter');
person.age = 3;
"age" in person; //true

=== vs ==

// == do coercion over primitives
// All primitives are truthy except: 0 NaN '' false null undefined
0 == false; //true
null == undefined; //true
false == false; //true
Boolean(1) == Boolean(0); //false
NaN == NaN; //false <- wierd
[1] == 1; // true
!!0 == !!'';  //true

//Objects are never coerced
new Boolean(false) == new Boolean(false); //false

//Rule of thumb: Always use ===

comparations with null

//Be careful with this:
function fn(x) {
  if (x) {...}
}
  
fn(false); //Probably this work as you are expecting, but ...

fn(0);  //What about this? Are you sure 0 is false?
        
//Better
if (x !== false)

//Same considerations can be done with
if (!x) {...}

//Better
if (x === false) {...}

//To check nullity, do not use
if (!x) {...}

//Instead use
if (x === undefined || x === null) {...}

//Rule of thumb: prefer undefined over null. Then you can use
if (x === undefined) {...}

//Be careful with this
fn(isEven) {
  isEven = isEven || true; //bad: there is no way to assign false
}
fn(false); //isEven will be true
fn('Peter'); //isEven will be "Peter"
                      
//Corrected and more secure
fn(isEven) {
  isEven = !!isEven || false; //truthy values will be true and falsy values will be false
}

Creating objects

  • JavaScript is a very powerful and flexible language
  • Objects can be created in different ways
  • In this section we are going to discuss a few of them

The problem of creating objects

//Note that we are using the object 'this'. We will talk more about 'this' later. By now you avoid making too many assumptions about it and its behavior, because it is quite different from other languages
var person1 = {
  name: 'peter',
  greet: function() {
    return 'Hi ' + this.name + '!';  
  }
};
JavaScript is a class-free language
We know that objects can be created literally

This approach has some drawbacks:

  • Code duplication
  • Public access to attributes
  • var person2 = {
      name: 'Anna',
      greet: function() {
        return 'Hi ' + this.name + '!';  
      }
    };
    person1.name = 'Michael';
    person1.lastName = 'Smith'; //bad, but possible
    delete person1.name; //worse

    Module pattern based solution

    function createPerson(spec) {
      "use strict";
      var name = spec.name;
      
      return Object.freeze({
        greet: function() {
          return 'Hi ' + name + '!';  
        }
      }); // Object.freeze() makes public interface immutable
    }

    Module pattern based solution (ii)

    var person1 = createPerson({name: 'Peter'});
    var person2 = createPerson({name: 'Anna'});
    
    person1.greet(); //"Hi Peter!"
    person1.name; //undefined
    
    delete person1.greet;
    person1.greet(); //"Hi Peter!"
    
    person1.lastName = 'Smith';
    person1.lastName; //undefined
    

    Advantages

    • Code reuse
    • Private access
    • Immutable interface
    • Avoid this and prototypes
    var person1 = createPerson({name: 'Peter'});
    var person2 = createPerson({name: 'Anna'});
    
    person1.greet === person2.greet; //false
    
    person1 instanceof Person; //Error Person is not defined

    Disadvantages

    • Waste of memory
    • Lost class concept

    Constructor functions solution

    function Person(name) {
      this.name = name;
      
      this.greet = function() {
        return 'Hi ' + this.name + '!';
      };
    }
    
    var person1 = new Person('Peter');
    person1.greet(); //"Hi Peter!"
    
    //This solution exposes the name variable
    person1.name; //"Peter"
    
    
    //But we can avoid this easily
    function Person(name) {
      var _name = name;
      
      this.greet = function() {
        return 'Hi ' + _name + '!';
      };
    
      //Object.freeze(this); //To add immutability
    }
    

    In JavaScript, any function can be used to construct objects if it is called with the 'new' operator

    It is generally accepted that the name of a constructor function begin with a capital letter

    Constructor functions are similar to constructors in classical OOP languages

    Constructor functions solution (ii)

    var person1 = new Person('Peter');
    person1 instanceof Person; //true
    person1 instanceof Object; //true
    typeof person1; //"object"

    Advantages

    • Code reuse
    • Private access
    • Immutable interface
    • Avoid prototypes
    • instanceof works
    var person1 = new Person('Peter');
    var person2 = new Person('Anna');
    
    person1.greet === person2.greet; //false

    Disadvantages

    • Waste of memory
    • Use of this is error prone

    prototype based solution

    function Person(name) {
      this.name = name;
    }
    
    Person.prototype.greet = function() {
      return 'Hi ' + this.name + '!';
    };
    
    var person1 = new Person('Peter');
    person1.greet(); //"Hi Peter!"

    JavaScript can emulate the class concept through prototypes

    prototype based solution (ii)

    var person1 = new Person('Peter');
    
    person1.greet === person2.greet; //true
    
    person1 instanceof Person; //true
    person1 instanceof Object; //true
    typeof person1; //"object"

    Advantages

    • efficient use of memory
    • instanceof works
    var person1 = new Person('Peter');
    
    person1.name = 'Anna';
    
    //Prototype does not change. Instead, a public `greet` variable is added to `this` object <- danger
    person1.greet = function() {
        return 'Bye!';
    }

    Disadvantages

    • Public access
    • Mutable object
    • Use of this is error prone

    Object.create() based solution

    function createPerson(name) {
      var personPrototype = {
        greet: function() {
          return 'Hi ' + this.name + '!';  
        }
      };
      
      return Object.create(personPrototype, {
        name: {
          value: name,
          writable: true
        }
      });
    }
    
    
    
    var person1 = new createPerson('Peter');
    person1.greet(); //"Hi Peter!"

    Object.create() based solution (ii)

    var person1 = new Person('Peter');
    
    person1.greet === person2.greet; //true
    
    person1 instanceof Person; //true
    person1 instanceof Object; //true
    typeof person1; //"object"

    Advantages

    • Similar to prototype solution
    • JavaScript preferred way
    var person1 = new Person('Peter');
    
    person1.name = 'Anna';
    
    //Prototype does not change. Instead, a public `greet` variable is added to `this` object <- danger
    person1.greet = function() {
        return 'Bye!';
    }

    Disadvantages

    • Weird for newbies
    • Public access
    • Use of this is error prone

    What solution should I use?

    • There is no right answer to this question
    • It depends on things like:
      • Personal code styling
      • Available memory
      • Is it important to protect the inadequate access?
      • Etc.
    • Anyway, you should know all methods
    • I think I prefer the first way (see this Douglas Crockford video).
    • Some key points:
      • Learn the JavaScript way and avoid doing it the way you are used to in other languages
      • JavaScript has bad parts and you are not forced to use them. For example: 'this' and 'new' were added to mimic the Java language but in  javascritpt really are not necessary

    Functions are objects

    function greet(firstName) {
      return 'Hi ' + first.name + '!';
    }
    
    greet instanceof Function; //true
    greet.name; //"greet"
    greet.length; //1 <- named parameters
    
    //Other attributes
    greet.prototype
    greet.call(...);
    greet.apply(...);
    greet.bind(...);
               
    //adding attributes           
    greet.greeting = 'Hi';
    greet.greeting; //"Hi"

    this object

    function Person(firstName) {
      this.firstName = firstName;
      
      this.greet = function(greeting) {
        return greeting + ' ' + this.firstName + '!';
      };
    }
    
    var person1 = new Person('Peter');
    
    //Seems like Java
    person1.firstName; //"Peter"
    
    //But something weird happens if we forget new operator
    var person2 = Person('Anna'); //No complainst
    
    //Person is called as a regular function that returns .... 
    person2; //undefined
    
    //firstName is attached to the global object
    "firstName" in window; //true -> bad

    this object (ii)

    function Person(firstName) {
    
      "use strict";
    
      this.firstName = firstName;
    
      ...  
    }
    
    var person2 = Person('Anna'); //"TypeError: Cannot set property 'firstName' of undefined"

    Fix one:

    function Person(firstName) {
      if (this instanceof Person) {
        this.firstName = firstName;
      } else {
        return new Person(firstName);
      }
    }
    
    var person2 = Person('Anna');
    person2.firstName; //"Anna"

    Fix two:

    function Person(firstName) {
      //Avoid using this object
      var _firstName = firstName;
      
      return {
        getFirstName: function() {
          return _firstName;
        }
      };
    }
    
    //We have seem this before:...
    //rename Person to createPerson
    var person2 = Person('Anna'); 
    person2.greet('Hi'); //"Hi Anna!"

    Fix three:

    this object (iv)

    function Person(firstName) {
      this.firstName = firstName;
    }
    
    Person.prototype.greet = function(greeting) {
      return return greeting + ' ' + this.firstName + '!';;
    };
    
    var person1 = new Person('Peter');
    
    //Seems it works
    person1.greet(); //"Hi Peter!"
    
    //But it does not
    var greet = Person.prototype.greet;
    greet('Hi'); //"Hi undefined!"
    
    //Why?
    
    //greet is a global function
    //when greet is executed, this becomes the global object <- weird
    

    this object (v)

    var person1 = new Person('Peter');
    
    var greet = Person.prototype.greet;
    
    //When greet function is called, it sets the this object to person1 passing 'Hi' as argument
    greet.call(person1, 'Hi'); //"Hi Peter!"

    Fix one:

    //apply is like call but the parameters are passed inside an array -> Useful in some situations
    greet.apply(person1, ['Hi']); //"Hi Peter!"

    Fix two:

    //bind returns a function with the 'this' object applied to the first param for later execution
    greetPeter = greet.bind(person1);
    greetPeter('Hi'); //"Hi Peter!"
    greetPeter('Bye'); //"Bye Peter"

    Fix three:

    //bind allows partial application parameters
    greetPeter = greet.bind(person1, 'Hi');
    
    //greeting parameter has been fixed previously
    greetPeter(); //"Hi Peter!"

    Fix four:

    this object (vi)

    function Person(firstName) {
      this.firstName = firstName;
      
      this.greets = {
        sayHello: function() {
          return 'Hello ' + this.firstName + '!';
        },
        sayBye: function() {
          return 'Bye ' + this.firstName + '!';
        }
        
      };
      
    }
    
    var person1 = new Person('Peter');
    
    //In sayHello function, this is bound to the global object
    person1.greets.sayHello('Hi'); //"Hello undefined!"

    this object (vii)

    function Person(firstName) {
      this.firstName = firstName;
      
      this.greets = {
        sayHello: function() {
          return 'Hello ' + this.firstName + '!';
        }.bind(this)
        ...
      };
    }

    Fix one:

    function Person(firstName) {
      this.firstName = firstName;
      
      var self = this;
      
      this.greets = {
        sayHello: function() {
          return 'Hello ' + self.firstName + '!';
        },
        ...
      };  
    }

    Fix two:

    person1.greets.sayHello.bind(person1)('Hi');

    Fix three:

    person1.greets.sayHello.call(person1, 'Hi');

    Fix four:

    this object (viii)

    function Person(firstName) {
      this.firstName = firstName;
      
      this.timedGreet = function(greet, milliseconds) {
        setTimeout(function() {
          console.log(greet + ' ' + this.firstName + '!');
        }, milliseconds);
      };
      
    }
    
    var person1 = new Person('Peter');
    
    person1.timedGreet('Hi', 1000); //"Hi undefined"

    this object (ix)

    function Person(firstName) {
      this.firstName = firstName;
      
      this.timedGreet = function(greet, milliseconds) {
        setTimeout(function() {
          console.log(greet + ' ' + this.firstName + '!');
        }.bind(this), milliseconds);
      };  
    }

    Fix one:

    function Person(firstName) {
      this.firstName = firstName;
      var self = this;
      this.timedGreet = function(greet, milliseconds) {
        setTimeout(function() {
          console.log(greet + ' ' + self.firstName + '!');
        }, milliseconds);
      };  
    }

    Fix two:

    function Person(firstName) {
      this.firstName = firstName;
      this.timedGreet = function(greet, milliseconds, self) {
        setTimeout(function() {
          console.log(greet + ' ' + self.firstName + '!');
        }, milliseconds);
      };  
    }
    
    var person1 = new Person('Peter');
    
    person1.timedGreet('Hi', 1000, person1);

    Fix three:

    JavaScript inheritance

    • JavaScript is a free class language, so...
    • In JavaScript there is no class inheritance
    • However there are some ways to mimic class inheritance​
    • In any case, why inheritance is necessary?
      • Code reuse
      • Polymorphism
      • Efficient use of memory
    • But inheritance also has its drawbacks
      • Lack of flexibility
      • Coupling
    • In fact, one of the GoF principles is:
      "Favor composition over inheritance"
    • Many JavaScript newbies are uncomfortable with prototypes and they demand something that seems like class inheritance
    • So let's discuss how to do this in JavaScript

    But seriously, think twice before doing this.

    JavaScript inheritance (ii)

    function Person(firstName) {
      this.firstName = firstName;
      
      this.getName = function() {
        return this.firstName;
      };
    }
    
    Person.prototype.greet = function(greeting) {
      return greeting + ' ' + this.firstName + '!';
    };
    
    function Student(firstName, grade) {
      //Questions?
      
      //1. How could Student inherit this.getName method from Person?
      //2. How could Student.prototype inherit from Person.prototype? 
    }

    Inheriting from 'this' object 

    function Student(firstName, grade) {
      Person.call(this, firstName);
      this.grade = grade;
    }
    
    var student1 = new Student('Peter');
    student1.firstName; //"Peter"
    student1.getName(); //"Peter"

    Inheriting from Parent.prototype 

    Student.prototype = Person.prototype;
    
    Student.prototype.getGrade = function() {
      return this.grade;
    };
    
    var person1 = new Person('Peter');
    "getGrade" in person1; //true <- Student.prototype and Person.prototype are the same object

    Bad solution one:

    Student.prototype = new Person();
    
    Student.prototype.getGrade = function() {
      return this.grade;
    };
    
    var person1 = new Person('Peter');
    "getGrade" in person1; //false
    
    var student1 = new Student('Anna', 'middle school');
    student1.getGrade(); //"middle school"
    student1.getName(); //"Anna"
    student1.greet('Hi'); //"Hi Anna!"
    
    //Seems all is ok, but...
    "firstName" in Student.prototype; //true

    Bad solution two (unfortunately widely used):

    Inheriting from Parent.prototype (ii)

    function Person(firstName) {
      this.firstName = firstName;  
      this.getName = function() {
        return this.firstName;
      };
    }
    
    Person.prototype.greet = function(greeting) {
      return greeting + ' ' + this.firstName + '!';
    };
    
    function Student(firstName, grade) {
      Person.call(this, firstName);
      this.grade = grade;
    }
    
    function F() {}
    F.prototype = Person.prototype;
    Student.prototype = new F();
    
    Student.prototype.getGrade = function() {
      return this.grade;
    };
    
    var person1 = new Person('Peter');
    "getGrade" in person1; //false
    
    var student1 = new Student('Anna', 'middle school');
    student1.getGrade(); //"middle school"
    student1.getName(); //"Anna"
    student1.greet('Hi'); //"Hi Anna!"
    "firstName" in Student.prototype; //false
    
    

    Good solution (mix the two bad solutions):

    Inheriting from Parent.prototype (iii)

    function inherits(Child, Parent) {
      function F() {}
      F.prototype = Parent.prototype;
      Child.prototype = new F();
      Child.prototype.constructor = Child;
    }
    
    function Student(firstName, grade) {
      Person.call(this, firstName);
      this.grade = grade;
    }
    
    inherits(Student, Person);
    
    Student.prototype.getGrade = function() {
      return this.grade;
    };
    
    var student1 = new Student('Anna', 'middle school');
    student1.getGrade(); //"middle school"
    student1.getName(); //"Anna"
    student1.greet('Hi'); //"Hi Anna!"
    "firstName" in Student.prototype; //false
    student1 instanceof Student; //true
    student1 instanceof Person; //true

    Generalizing the problem:

    Inheriting from Parent.prototype (iv)

    personPrototype = {
      greet: function(greeting) {
        return greeting + ' ' + this.firstName + '!';
      }
    };
    
    
    var studentPrototype = Object.create(personPrototype, {
      getGrade: {
        value: function() {
          return this.grade;
        }
      }
    });
    
    function createStudent(firstName, grade) {  
      return Object.create(studentPrototype, {
        firstName: {
          value: firstName
        },
        grade: {
          value: grade
        }
      });
    }
    
    var student1 = createStudent('Anna', 'Middle school');
    
    student1.getGrade(); //"middle school"
    student1.greet('Hi'); //"Hi Anna!"
    

    Better approach (using Object.create):

    Avoiding classical heritage

    function greet(person, greeting) {
      return greeting + ' ' + person.firstName + '!';
    }
    
    var createStudent = function(firstName, grade) {
      var self = {
        firstName: firstName,
        grade: grade
      };
    
      return Object.freeze({
        getGrade: function() {
          return self.grade;
        },
        greet: greet.bind(null, self)
      });
    };
    
    var student1 = createStudent('Anna', 'Middle school');
    
    student1.getGrade(); //"middle school"
    student1.greet('Hi'); //"Hi Anna!"
    
    //Memory waste
    var student2 = createStudent('Peter', 'High school');
    student1.greet === student2.greet; //false
    student1.getGrade === student2.getGrade; //false

    Why mimic class-based languages if JavaScript is classless?

    Avoiding classical heritage (ii)

    function greet(greeting) {
      return greeting + ' ' + this.firstName + '!';
    }
    
    var createStudent = (function() {
      var studentPrototype = {
        greet: greet,
        getGrade: function() {
          return this.grade;
        }
      };
      return function(firstName, grade) {
        return Object.create(studentPrototype, {
          firstName: {
            value: firstName
          },
          grade: {
            value: grade
          }
        });
      };
    }());
    
    var student1 = createStudent('Anna', 'Middle school');
    student1.getGrade(); //"middle school"
    student1.greet('Hi'); //"Hi Anna!"
    
    var student2 = createStudent('Peter', 'High school');
    student1.greet === student2.greet; //true
    student1.getGrade === student2.getGrade; //true
    
    //public access
    "firstName" in student1; //true
    "grade" in student1; //true

    Prototype solution:

    Array-like objects

    var persons = {
      0: 'Peter',
      1: 'Anna',
      2: 'Michael',
      length: 3
    };
    
    Array.prototype.map.call(persons, function(name) {
      return name.toUpperCase();
    }); //["PETER", "ANNA", "MICHAEL"]

    We can benefit from the array methods in objects that seem arrays

    Array-like objects (ii)

    function add(x, y) {
      return x + Number(y);
    }
    
    var number = "1234";
    
    Array.prototype.reduce.call(number, add, 0); //10

    Strings are array-like objects

    function add(x, y) {
      return x + y;
    }
    
    function sum() {
      return Array.prototype.reduce.call(arguments, add, 0);
    }
    
    sum(1, 2, 3, 4); //10

    arguments is an array-like object

    Array-like objects (iii)

    Array.prototype.forEach.call(document.querySelectorAll('div'), addClickEvent);
    
    function addClickEvent(div) {  
      div.addEventListener('click', function(event) {
        event.target.style.backgroundColor = 'Green';
      });
    }

    DOM Collections are array-like objects

    apply as arguments

    function min() {
      return Math.min.apply(null, arguments);
    }
    
    min(1, 2, 3); //1

    We can use Function.prototype.apply with arguments

    Async JavaScript

    • In JavaScript when a slow I/O action is completed, it is added to a queue called Event Loop (see this video)
    • Advantages:
      • No time wasted waiting
      • No race conditions
      • No deadlocks
      • Super easy: No synchronized variables, semaphores, etc
    • Disadvantages:
      • JavaScript apps are single threaded: waste CPU
      • No real concurrency

    Event Loop

    In JavaScript there are different ways to perform pseudo-concurrency:

    • Callbacks
    • Promises
    • Events
    • Generators

    Async programming in JavaScript

    Callbacks allow us to decide the continuation function when the asynchronous operation is complete. 

    Callbacks

    setTimeout(function() {
      console.log('Done!');
    }, 1000);
    
    
    fs.readFile('/etc/passwd', function (err, data) {
      if (err) throw err;
      console.log(data);
    });
    
    
    $.ajax(url, {
      success: function(...) { ... },
      error: function(...) { ... }
    });

    Callbacks (ii)

    Callbacks are easy but they have some drawbacks:

    • Callback hell
    • Callback functions can not return a value (passing style)
    • Errors get lost easily (try-catch does not work)
    • Doing sequences in parallel is hard
    • Difficult to debug

    One solution: Async.js

    async.map(['file1','file2','file3'], fs.stat, function(err, results){
        // results is now an array of stats for each file
    });
    
    async.filter(['file1','file2','file3'], fs.exists, function(results){
        // results now equals an array of the existing files
    });
    
    async.parallel([
        function(){ ... },
        function(){ ... }
    ], callback);
    
    async.series([
        function(){ ... },
        function(){ ... }
    ]);

    Promises seem synchronous code

    Promises

    checkUser(credentials).then(loginSuccess, loginError).then(gotoMainPage);
    
    $.ajax(url).done(function() {...}).fail(function() {...});

    Q: a promises implementation

    Q.fcall(promisedStep1)
    .then(promisedStep2)
    .then(promisedStep3)
    .then(promisedStep4)
    .then(function (value4) {
        // Do something with value4
    })
    .catch(function (error) {
        // Handle any error from all above steps
    })
    .done();

    Example of naive promise implementation

    Promises (ii)

    function Promise() {
        this._dones = [];
        this._errors = [];
    }
    Promise.prototype.done = function (onSuccess) {
        this._dones.push(onSuccess);
        return this;
    };
    
    Promise.prototype.error = function (onError) {
        this._errors.push(onError);
        return this;
    };
    
    Promise.prototype.resolve = function (result) {
        this._complete('resolve', result);
    };
     
    Promise.prototype.reject = function (error) {
        this._complete('reject', error);
    };
    
    Promise.prototype._complete = function (action, data) {
        // disallow multiple calls to resolve or reject
        this.resolve = this.reject = function () {
            throw new Error('Promise already completed.');
        };
        
        var functions = action === 'resolve' ? this._dones : this._errors;
    
        functions.forEach(function(fn) {
            fn(data);
        });
        
        this._dones = [];
        this._errors = [];
    };

    Example of use naive promise implementation

    Promises (iii)

    function doWork(time, callback) {
        if (typeof time !== 'number' || time <= 0) {
            setTimeout(function() {            
                callback('Bad format', null);
            }, 200); //Simulates a failure
        } else {
            setTimeout(function() {
                callback(null, 'Done!');
            }, time); //Simulates a long work
        }
    }
    
    function work(time) {
        var promise = new Promise();
        doWork(time, function(error, result) {
            if(error) {
                promise.reject(error);
            } else {
                promise.resolve(result);
            }
        });
        return promise;
    }
    work(2000).done(function(result) {
        console.log(result);
    }).error(function(error) {
        console.log(error);
    });
    
    work(4000).done(function(result) {
        console.log(result);
    }).done(function(result) {
        console.log("In second success handler");
    }).error(function(error) {
        console.log(error);
    });
    
    work(-2000).done(function(result) {
        console.log(result);
    }).error(function(e) {
        console.log("In first error handler");
    }).error(function(e) {
        console.log(e);
    });

    Promises (iv)

    Why promises are so cool?

    • Fluent/Functional style
    • Easy aggregation/composition
    • Style-error bubbling

    Events are great when there are multiple receivers (listeners) interested  on the response:

    Events

    document.addEventListener('click', function(event) {....});
    
    
    $('#table').on( "click", function() {
      ...
    });
    
    
    var server = http.createServer(function (req, res) {
      ...
      req.on('data', function (chunk) {
        ...
      });
    
      req.on('end', function () {
        ...
      });
      ...
    });
    

    Example of naive events implementation

    Events (ii)

    function EventEmitter() {
        var events = {};
        this.addListener = function (event, listener) {
            if (!events[event]) {
                events[event] = [];
            }
            events[event].push(listener);
        };
        this.emitEvent = function (event) {
            if (events[event]) {
                events[event].forEach(function(listener) {
                    listener(event);
                });
            }
        };
    }

    Example of use naive events implementation

    Events (iii)

    var ee = new EventEmitter();
    
    function listener1(event) {
        console.log('The ' + event + ' event has been recived by listener1.');
    }
    
    function listener2(event) {
        console.log('The ' + event + ' event has been recived by listener2.');
    }
    
    ee.addListener('foo', listener1); //Receiver
    ee.addListener('foo', listener2); //Receiver
    ee.addListener('bar', listener1); //Receiver
    ee.emitEvent('foo'); //Emitter
    ee.emitEvent('bar'); //Emitter

    Events (iv)

    Benefits of using events

    • Multiple receivers
    • Easy add/remove listeners
    • Decoupling between emitter and receiver

    Cons of using events

    • Difficult to debug/test
    • Easy to lost control: chain of events
    • Difficult to handle race conditions

    New player in ECMAScript 6

    Generators

    function* foo(x) {
        yield x + 1;
    
        var y = yield null;
        return x + y;
    }
    
    var gen = foo(5);
    gen.next(); // { value: 6, done: false }
    gen.next(); // { value: null, done: false }
    gen.send(8); // { value: 13, done: true }

    Co: a generators implementation

    co(function *(){
      try {
        var res = yield get('http://badhost.invalid');
        console.log(res);
      } catch(e) {
        console.log(e.code) // ENOTFOUND
     }
    })()

    Generators (ii)

    Pros

    • Seem synchronous code
    • Simplified lazy evaluation
    • Allow infinite sequences

    Cons

    • Immature technology
    • Awful performance
    • Are they really neccesary?

    Example

    Inspired from this article

     

    Solve this problem:

    var findLargest = require('./findLargest');
    findLargest('./path/to/dir', function (er, filename) {
      if (er) return console.error(er);
      console.log('largest file was:', filename);
    });

    Example (ii)

    Nested callback approach:

    var fs = require('fs');
    var path = require('path');
     
    module.exports = function (dir, cb) {
      fs.readdir(dir, function (err, files) {
        if (err) return cb(err);
        var counter = files.length;
        var errored = false;
        var stats = [];
     
        files.forEach(function (file, index) {
          fs.stat(path.join(dir, file), function (er, stat) { // <- parallel process
            if (errored) return;
            if (er) {
              errored = true;
              return cb(er);
            }
            stats[index] = stat;
            if (--counter == 0) {
              var largest = stats
                .filter(function (stat) { return stat.isFile(); })
                .reduce(function (prev, next) {
                  return prev.size > next.size ?
                    prev :
                    next;
                }, 0);
              cb(null, files[stats.indexOf(largest)]);
            }
          });
        });
      });
    };

    Example (iii)

    Modular callback approach:

    var fs = require('fs');
    var path = require('path');
     
    module.exports = function (dir, cb) {
      fs.readdir(dir, function (er, files) {
        if (er) return cb(er);
        var paths = files.map(function (file) {
          return path.join(dir,file);
        });
     
        getStats(paths, function (er, stats) {
          if (er) return cb(er);
          var largestFile = getLargestFile(files, stats);
          cb(null, largestFile);
        });
      });
    };
    function getStats (paths, cb) {
      var counter = paths.length;
      var errored = false;
      var stats = [];
      paths.forEach(function (path, index) {
        fs.stat(path, function (er, stat) {
          if (errored) return;
          if (er) {
            errored = true;
            return cb(er);
          }
          stats[index] = stat;
          if (--counter == 0) cb(null, stats);
        });
      });
    }
    
    function getLargestFile (files, stats) {
      var largest = stats
        .filter(isFile)
        .reduce(max.bind(null, 'size'), 0);
      return files[stats.indexOf(largest)];
    }
    
    function max(prop, x, y) {
      return x[prop] > y [prop] ?
        x :
        y;
    }
    
    function isFile(stat) {
      return stat.isFile();
    }

    Example (iv)

    Async library approach:

    var fs = require('fs');
    var async = require('async');
    var path = require('path');
     
    module.exports = function (dir, cb) {
      async.waterfall([
        function (next) {
          fs.readdir(dir, next)
        },
        function (files, next) {
          var paths = files.map(function (file) { return path.join(dir, file) });
          async.map(paths, fs.stat, function (er, stats) { 
            next(er, files, stats);
          });
        },
        function (files, stats, next) {
          var largest = stats
            .filter(isFile)
            .reduce(max.bind(null, 'size'), 0);
    
          next(null, files[stats.indexOf(largest)]);
        }
      ], cb);
    }
    
    function max(prop, x, y) {
      return x[prop] > y [prop] ?
        x :
        y;
    }
    
    function isFile(stat) {
      return stat.isFile();
    }

    Example (v)

    Q promises library approach:

    var fs = require('fs');
    var path = require('path');
    var Q = require('q');
    var fs_readdir = Q.denodeify(fs.readdir);
    var fs_stat = Q.denodeify(fs.stat);
     
    module.exports = function (dir) {
      return fs_readdir(dir)
        .then(function (files) {
          
          var promises = files.map(function (file) {
            return fs_stat(path.join(dir,file));
          });
    
          return Q.all(promises).then(function (stats) {
            return [files, stats];
          });
        })
       .then(function (data) {
          var files = data[0]
          var stats = data[1]
          var largest = stats.filter(isFile)
            .reduce(max.bind(null, 'size'), 0);
          return files[stats.indexOf(largest)];
        });
    };
    
    function max(prop, x, y) {
      return x[prop] > y [prop] ? x : y;
    }
    
    function isFile(stat) {
      return stat.isFile();
    }
    //use
    var findLargest = require('./findLargest')
    findLargest('./path/to/dir')
      .then(function (filename) {
        console.log('largest file was:', filename);
      })
      .catch(console.error);

    Example (v)

    Co generator library approach:

    var co = require('co');
    var thunkify = require('thunkify');
    var fs = require('fs');
    var path = require('path');
    var readdir = thunkify(fs.readdir);
    var stat = thunkify(fs.stat);
     
    module.exports = co(function* (dir) {
      var files = yield readdir(dir);
      var stats = yield files.map(function (file) {
        return stat(path.join(dir,file))
      });
      var largest = stats
        .filter(isFile)
        .reduce(max.bind(null, 'size'), 0);
      return files[stats.indexOf(largest)];
    });
    
    function max(prop, x, y) {
      return x[prop] > y [prop] ?
        x :
        y;
    }
    
    function isFile(stat) {
      return stat.isFile();
    }

    ECMAScript 6 is coming

    Test ECMAScript 6 today

    $ sudo npm install --global traceur
    
    $ traceur --out build.js --script my_source_file.js
    
    $ node build.js

    Arrow Functions

    var users = [
      { name: 'Jack', age: 21 },
      { name: 'Ben', age: 23 },
      { name: 'Adam', age: 22 }
    ];
    
    console.log(users.map(function(user) { return user.age; }));// [21, 23, 22]
    
    var ages = users.map(user => user.age);
    var sum = ages.reduce((a, b) => a + b);
    console.log(sum); // 66
    
    var agesDoubled = users.map(user => {
      var age = user.age;
      return age * 2;
    });
    
    console.log(agesDoubled); // [42, 46, 44]

    Arrow Functions (ii)

    //Lexical this binding
    
    var person = {
      name: 'Peter',
      getName: function() {
        return this.name;
      }
    };
    
    console.log(person.getName()); // "Peter"
    
    var getPeterName = person.getName;
    
    console.log(getPeterName()); // "undefined" [in Node]
    
    var person = {
      name: 'Peter',
      getName: () => {
        return this.name;
      }
    };
    
    console.log(person.getName()); // "Peter"
    
    var getPeterName = person.getName;
    
    console.log(getPeterName()); // "Peter"
    

    Modules

    // lib/math.js
    export function sum(x, y) {
      return x + y;
    }
    export var pi = 3.141593;
    // app.js
    module math from "lib/math";
    console.log("2Ï€ = " + math.sum(math.pi, math.pi));
    // otherApp.js
    import {sum, pi} from "lib/math";
    alert("2Ï€ = " + sum(pi, pi));
    // lib/mathplusplus.js
    export * from "lib/math";
    export var e = 2.71828182846;
    export default function(x) {
        return Math.exp(x);
    }
    // app.js
    module math from "lib/mathplusplus";
    import exp from "lib/mathplusplus";
    alert("2Ï€ = " + exp(math.pi, math.e));
    // Dynamic loading – ‘System’ is default loader
    System.import('lib/math').then(function(m) {
      alert("2Ï€ = " + m.sum(m.pi, m.pi));
    });

    Map + Set

    // Sets
    var s = new Set();
    s.add("hello").add("goodbye").add("hello");
    s.size === 2;
    s.has("hello") === true;
    
    // Maps
    var m = new Map();
    m.set("hello", 42);
    m.set(s, 34);
    m.get(s) == 34;

    Weak Maps + Weaks Sets

    var map = new WeakMap(),
        element = document.querySelector(".element");
    
    map.set(element, "Original"); //key must be a non primitive, value can be of any type
    
    // later
    var value = map.get(element);
    console.log(value);             // "Original"
    
    // later still - remove reference
    element.parentNode.removeChild(element);
    element = null;
    
    value = map.get(element);
    console.log(value);             // undefined <- avoids memory leaks
    
    
    // Weak Sets
    var ws = new WeakSet();
    ws.add({ data: 42 });
    // Because the added object has no other references, it will not be held in the set

    Block-scoped variables

    for (var i = 0; i < 10; i++);
    console.log(i); // 10
    
    for (let j = 0; j < 10; j++);
    console.log(j); // Error: j is not defined

    Constants

    const x = 4;
    x = 9; // Error: x is read-only

    Classes

    class SkinnedMesh extends THREE.Mesh {
      constructor(geometry, materials) {
        super(geometry, materials);
    
        this.idMatrix = SkinnedMesh.defaultMatrix();
        this.bones = [];
        this.boneMatrices = [];
        //...
      }
      update(camera) {
        //...
        super.update();
      }
      static defaultMatrix() {
        return new THREE.Matrix4();
      }
    }
    // Pseudo-code of Array
    class Array {
        constructor(...args) { /* ... */ }
        static [Symbol.create]() {
            // Install special [[DefineOwnProperty]]
            // to magically update 'length'
        }
    }
    
    // User code of Array subclass
    class MyArray extends Array {
        constructor(...args) { super(...args); }
    }
    
    // Two-phase 'new':
    // 1) Call @@create to allocate object
    // 2) Invoke constructor on new instance
    var arr = new MyArray();
    arr[1] = 12;
    arr.length == 2

    Enhanced Object Literals

    var obj = {
        // __proto__
        __proto__: theProtoObj,
        // Shorthand for ‘handler: handler’
        handler,
        // Methods
        toString() {
         // Super calls
         return "d " + super.toString();
        },
        // Computed (dynamic) property names
        [ 'prop_' + (() => 42)() ]: 42
    };

    Template Strings

    // Basic literal string creation
    `In JavaScript '\n' is a line-feed.`
    
    // Multiline strings
    `In JavaScript this is
     not legal.`
    
    // Construct a DOM query
    var name = "Bob", time = "today";
    `Hello ${name}, how are you ${time}?`

    Destructuring

    // list matching
    var [a, , b] = [1,2,3];
    
    // object matching
    var { op: a, lhs: { op: b }, rhs: c }
           = getASTNode()
    
    // object matching shorthand
    // binds `op`, `lhs` and `rhs` in scope
    var {op, lhs, rhs} = getASTNode()
    
    // Can be used in parameter position
    function g({name: x}) {
      console.log(x);
    }
    g({name: 5})
    
    // Fail-soft destructuring
    var [a] = [];
    a === undefined;
    
    // Fail-soft destructuring with defaults
    var [a = 1] = [];
    a === 1;

    Default + Rest + Spread

    
    function f(x, y=12) {
      // y is 12 if not passed (or passed as undefined)
      return x + y;
    }
    f(3); // 15
    
    function f(x, ...y) {
      // y is an Array
      return x * y.length;
    }
    f(3, "hello", true); // 6
    
    function f(x, y, z) {
      return x + y + z;
    }
    // Pass each elem of array as argument
    f(...[1,2,3]); //6

    Iterators

    //Classical way
    function idMaker(){
        var index = 0;
        
        return {
           next: function(){
               return {value: index++, done: false};
           }
        }
    }
    
    var it = idMaker();
    
    console.log(it.next().value); // '0'
    console.log(it.next().value); // '1'
    console.log(it.next().value); // '2'
    // ...
    
    //New way
    function* idMaker(){
        var index = 0;
        while(true)
            yield index++;
    }
    
    var gen = idMaker();
    
    console.log(gen.next().value); // '0'
    console.log(gen.next().value); // '1'
    console.log(gen.next().value); // '2'
    // ...
    

    Iterators (ii)

    function* fibonacci() { // a generator function
        let [prev, curr] = [0, 1];
        for (;;) {
            [prev, curr] = [curr, prev + curr];
            yield curr;
        }
    }
    
    for (let n of fibonacci()) {
        // truncate the sequence at 1000
        if (n > 1000)
            break;
        print(n);
    }
    let fibonacci = {
      [Symbol.iterator]() {
        let pre = 0, cur = 1;
        return {
          next() {
            [pre, cur] = [cur, pre + cur];
            return { done: false, value: cur }
          }
        }
      }
    }
    
    for (var n of fibonacci) {
      // truncate the sequence at 1000
      if (n > 1000)
        break;
      print(n);
    }

    Iterators (iii)

    let articleParagraphs = document.querySelectorAll("article > p");
    
    for (let paragraph of articleParagraphs) {
      paragraph.classList.add("read");
    }

    Comprehensions

    [for (i of [ 1, 2, 3 ]) i*i ]; // [ 1, 4, 9 ]
    
    var abc = [ "A", "B", "C" ];
    [for (letters of abc) letters.toLowerCase()]; // [ "a", "b", "c" ]
    
    var years = [ 1954, 1974, 1990, 2006, 2010, 2014 ];
    [for (year of years) if (year > 2000) year]; // [ 2006, 2010, 2014 ]
    
    var numbers = [ 1, 2, 3 ];
    var letters = [ "a", "b", "c" ];
    
    var cross = [for (i of numbers) for (j of letters) i+j];
    // [ "1a", "1b", "1c", "2a", "2b", "2c", "3a", "3b", "3c" ]

    Proxies

    // Proxying a normal object
    var target = {};
    var handler = {
      get: function (receiver, name) {
        return `Hello, ${name}!`;
      }
    };
    
    var p = new Proxy(target, handler);
    p.world === 'Hello, world!';
    / Proxying a function object
    var target = function () { return 'I am the target'; };
    var handler = {
      apply: function (receiver, ...args) {
        return 'I am the proxy';
      }
    };
    
    var p = new Proxy(target, handler);
    p() === 'I am the proxy';

    Private object properties

    var firstName = Symbol("first name property");
    var person = {};
    
    person[firstName] = "Nicholas";
    console.log(person[firstName]);     // "Nicholas" 
    
    //You need the symbol to access the property
    console.log(Symbol("first name property")); //undefined

    Math + Number + String + Object APIs

    Number.EPSILON
    Number.isInteger(Infinity) // false
    Number.isNaN("NaN") // false
    
    Math.acosh(3) // 1.762747174039086
    Math.hypot(3, 4) // 5
    Math.imul(Math.pow(2, 32) - 1, Math.pow(2, 32) - 2) // 2
    
    "abcde".contains("cd") // true
    "abc".repeat(3) // "abcabcabc"
    
    Array.from(document.querySelectorAll('*')) // Returns a real Array
    Array.of(1, 2, 3) // Similar to new Array(...), but without special one-arg behavior
    [0, 0, 0].fill(7, 1) // [0,7,7]
    [1,2,3].findIndex(x => x == 2) // 1
    ["a", "b", "c"].entries() // iterator [0, "a"], [1,"b"], [2,"c"]
    ["a", "b", "c"].keys() // iterator 0, 1, 2
    ["a", "b", "c"].values() // iterator "a", "b", "c"
    
    Object.assign(Point, { origin: new Point(0,0) })

    Promises

    function timeout(duration = 0) {
        return new Promise((resolve, reject) => {
            setTimeout(resolve, duration);
        })
    }
    
    var p = timeout(1000).then(() => {
        return timeout(2000);
    }).then(() => {
        throw new Error("hmm");
    }).catch(err => {
        return Promise.all([timeout(100), timeout(200)]);
    });

    Tail Call recursion

    function factorial(n, acc = 1) {
        'use strict';
        if (n <= 1) return acc;
        return factorial(n - 1, n * acc);
    }
    
    // Stack overflow in most implementations today,
    // but safe on arbitrary inputs in eS6
    factorial(100000);

    JavaScript Testing

    • Confidence
    • Easier refactoring
    • Less regression
    • Less complexity
    • TDD is fun!

    Why test?

    • First law: You may not write production code until you have written a failing unit test
    • Second law: You may not write more of a unit test that is sufficient to fail
    • Third law: You may not write more production code than is sufficient to pass the currently failing test

    How to test?

    • Keep test clean: Test code is just as important as production code
    • Clean tests: readability
    • One assert per test: Tests come to a single conclusion  that is quick and easy to understand
    • F.I.R.S.T.
      • Fast
      • Independent
      • Repeatable
      • Self-validating
      • Timely

    How to test (ii)

    Better than more theory, we are going to practice TDD with an exercise. You can find the complete solution in here.

    TDD by Example

    This is the description:

    Algebraic Data Types (not to be confused with Abstract Data Types, ADT) are composed types (normally recursively) and for those who have defined a serie of operations.

    For example in Haskell, natural numbers can be defined in this way:

    data Nat = Zero | Succ Nat

    So:

    zero = Zero
    one = Succ Zero
    two = Succ (Succ Zero)
    three = Succ (Succ (Succ Zero))
    ...

    The objetive of this kata is to define an algebra for the Nat data type.

    TDD by Example (ii)

    In JavaScript we are going to use the next definition for natural numbers:

    function zero(){}
    
    function succ(nat) {
      return function() {
        return nat;
      };
    }

    zero and succ() are constructor functions of the Nat data type. A nat number could be the zero function (although you will never really call this function) or a function obtained by calling the function succ() with the previous natural number. This definition corresponds roughly to the definition of Church numbers

     

    zero nat number corresponds to zero function

    one nat number is obtained by calling succ(zero)

    two nat number is obtained by calling succ(succ(zero))

    ... and so on

    TDD by Example (iii)

    Or more precisely:

    var one = succ(zero);
    var two = succ(one) = succ(succ(zero));
    ...

    The algebra are composed by the next operations:

    natToInt(nat) -> int // obtains the integer value of the nat number. For example natToInt(succ(succ(zero)) returns 2
    intToNat(int) -> nat // converts an integer to a natural value. For example intToNat(2) does the same that succ(succ(zero))
    toString(nat) -> string // returns a string representation of the natural value (see examples below)
    add(nat1, nat2) -> nat // given two natual numbers, returns the natural sum of them
    mul(nat1, nat2) -> nat // multiplies two naturals
    compareTo(nat1, nat2) -> int // returns a number less than zero when nat1 < nat2; greater than zero when nat1 > nat2; and 0 when nat1 === nat2

    TDD by Example (iv)

    Some examples:

    natToInt(zero); // 0
    natToInt(succ(zero)); // 1
    natToInt(succ(succ(zero))); // 2 
    
    intToNat(0) === zero; // true
    intToNat(1); // is the same than succ(zero)
    intToNat(2); // is the same than succ(succ(zero))
    natToInt(intToNat(10)); // 10
    
    toString(zero); // "zero"
    toString(succ(zero)); // "succ(zero)"
    toString(succ(succ(zero))); // "succ(succ(zero))"
    
    add(zero, zero) === zero; // true
    add(succ(zero), zero); // is the same that succ(zero)
    add(succ(zero), succ(zero)); // is the same that succ(succ(zero))
    add(succ(zero), succ(succ(zero))); // is the same that succ(succ(succ(zero)))
    natToInt(add(zero, succ(zero))); // 1
    natToInt(add(intToNat(10), intToNat(15))); // 25
    
    mul(zero, zero) === zero; // true
    mul(suc(zero), zero) === zero; // true
    natToInt(mul(intToNat(10), intToNat(15))); // 150
    
    compareTo(zero, zero); // 0
    compareTo(zero, succ(zero)) < 0; // true
    compareTo(succ(zero), 0) > 0; // true
    compareTo(suc(zero), succ(zero)); // 0
    natToInt(compareTo(intToNat(10), intToNat(15))) < 0; // true

    TDD by Example (v)

    Additional notes:

    • Function implementations must be pure (no mutable state and no side effects). So iterative implementations are not allowed. Do not use while or for keywords in your solution.
    • A simple but inefficient implementation of add() operation could be:
    function add(nat1, nat2) {
      return intToNat(natToInt(nat1) + natToInt(nat2));
    }

    But you must not use arithmetic operators (+, - , *, /, %, ...) in implementing the functions add() and mul().

    TDD by Example (vi)

    Let's start with the first function: natToInt()

    This could be the very first dummy test:

    var assert = require("assert");
    
    var zero = function(){};
    
    function succ(nat) {
      return function() {
        return nat;
      };
    }
    
    describe('Nat numbers tests', function(){
      describe('natToInt() tests', function(){
        it('should return 0 for the zero function', function(){
          assert.equal(natToInt(zero), 0);
        });
      });
    });
    Obviously test fails (red), since we have not defined the natToInt() function:
    $ mocha test.js
    
     Nat numbers tests
        natToInt() tests
          1) should return 0 for the zero function
    
    
      0 passing (6ms)
      1 failing
    
      1) Nat numbers tests natToInt() tests should return 0 for the zero function:
         ReferenceError: natToInt is not defined
          at Context.<anonymous> (/home/mint/test.js:14:20)

    TDD by Example (vii)

    Now, we can write a simple implementation to pass the test:

    ...
    function  natToInt(nat) {
      return 0;
    }
    
    describe('Nat numbers tests', function(){
      describe('natToInt() tests', function(){
        it('should return 0 for the zero function', function(){
          assert.equal(natToInt(zero), 0, 'natToInt(zero) should return 0');
        });
      });
    });
    If we try again, the test pass (green):
    $ mocha test.js 
    
    
      Nat numbers tests
        natToInt() tests
          ✓ should return 0 for the zero function 
    
    
      1 passing (8ms)

    TDD by Example (viii)

    This implementation is not very useful since it only applies to the number zero. So we go back to cause a failure by adding a test case.

    ...
    var one = succ(zero);
    
    describe('Nat numbers tests', function(){
      describe('natToInt() tests', function(){
    
        it('should return 0 for the zero function', function(){
          assert.equal(natToInt(zero), 0, 'natToInt(zero) should return 0');
        });
    
        it('should return 1 for the one nat number', function(){
          assert.equal(natToInt(one), 1, 'natToInt(one) should return 1');
        });
      });
    });
    Now the test fails:
     $ mocha test.js 
    
    
      Nat numbers tests
        natToInt() tests
          ✓ should return 0 for the zero function 
          1) should return 1 for the one nat number
    
    
      1 passing (7ms)
      1 failing
    
      1) Nat numbers tests natToInt() tests should return 1 for the one nat number:
         AssertionError: natToInt(one) should return 1
         at Context.<anonymous> (/home/mint/test.js:24:14)

    TDD by Example (ix)

    We can fix this by changing the implementation:

    ...
    function  natToInt(nat) {
      if (nat === zero) return 0;
      else return 1;
    }
    $ mocha test.js 
    
    
      Nat numbers tests
        natToInt() tests
          ✓ should return 0 for the zero function 
          ✓ should return 1 for the one nat number 
    
    
      2 passing (8ms)

    TDD by Example (x)

    Easily we can break the implementation by adding a new test:

    ...
    var one = succ(zero);
    var two = succ(one);
    ...
    it('should return 2 for the two nat number', function(){
      assert.equal(natToInt(two), 2, 'natToInt(two) should return 2');
    });

    This procedure seems pointless. But it is just the opposite, because it helps us reason about the correct solution.

    TDD by Example (xi)

    Moreover, the solution is not as simple as it seems:

    ...
    function  natToInt(nat) {
      if (nat === zero) return 0;
      if (nat === one) return 1;
      else return 2;
    }
    Now the test still fails:
    $ mocha test.js 
    
    
      Nat numbers tests
        natToInt() tests
          ✓ should return 0 for the zero function 
          1) should return 1 for the one nat number
          ✓ should return 2 for the one nat number 
    
    
      2 passing (8ms)
      1 failing
    
      1) Nat numbers tests natToInt() tests should return 1 for the one nat number:
         AssertionError: natToInt(one) should return 1
    This is interesting, we've broken a test that was running before

    TDD by Example (xii)

    Why? Very easy, check out the code:

    var zero = function(){};
    var one = succ(zero);
    var two = succ(one);
    
    function succ(nat) {
      return function() {
        return nat;
      };
    }
    
    function  natToInt(nat) {
      if (nat === zero) return 0;
      if (nat === one) return 1;
      else return 2;
    }
    The succ() function always returns a new object (functions are objects) and, in JavaScript, objects are always compared  by reference. So:
    succ(zero) === succ(zero); //false
    zero === zero; //true
    In our case, the only object that can be compared with the === operator, is zero.
    In fact, that's the usefulness of compareTo() function, which later we will have to implement. This function will allow us compare natural numbers avoiding === operator.

    TDD by Example (xiii)

    So let's make a first attempt to seriously implement the natToInt() function:

    function natToInt(nat) {
      var result = 0;
      while (nat !== zero) {
        nat = nat();
        result++;
      }
      return result;
    }

    This is not a very FP approach, but it is working.

    However, it violates one of the requirements: "iterative implementations are not allowed. Do not use while or for keywords in your solution"

    So we'll add a test to invalidate the solution:

    it('should not use loops', function(){
      assert.ok(/for|while/.test(natToInt.toString()) === false, '"for" or "while" keywords are not allowed');
    });
    $ mocha test.js 
    
      Nat numbers tests
        natToInt() tests
          ✓ should return 0 for the zero function 
          ✓ should return 1 for the one nat number 
          ✓ should return 2 for the one nat number 
          1) should ot use loops
    
    
      3 passing (7ms)
      1 failing
    
      1) Nat numbers tests natToInt() tests should ot use loops:
         AssertionError: "for" or "while" keywords are not allowed

    TDD by Example (xiv)

    A recursive implementation for pass the tests would be :

    function natToInt(nat) {
      return nat === zero ? 0 : 1 + natToInt(nat());
    }

    It is time that you are going to implement the rest of the functions.

    Now we have enough confidence that the natToInt() function behaves correctly.

    You can use this function in the implementation or testing the rest of the exercise as we know it will not fail (really you will use it only in tests).

    We can also make changes in the implementation safely

    When you have finished this kata, you can continue with this and with this more challenging katas.

    Resources guide

    Books

    Videos

    Online Help

    Blogs

    Online Learning

    Online Playgrounds

    IDEs

    Testing

    Testing (ii)

    Testing (iii)

    Package Managers

    Module Loaders

    Task Automation

    Scaffolding Tools

    Functional Libraries

    Reactive Programming

    Async JavaScript

    JavaScript Compile Languages

    JavaScript Engines

    JavaScript Hardware

    Resources of resources