Book Image

Hands-on Machine Learning with JavaScript

Book Image

Hands-on Machine Learning with JavaScript

Overview of this book

In over 20 years of existence, JavaScript has been pushing beyond the boundaries of web evolution with proven existence on servers, embedded devices, Smart TVs, IoT, Smart Cars, and more. Today, with the added advantage of machine learning research and support for JS libraries, JavaScript makes your browsers smarter than ever with the ability to learn patterns and reproduce them to become a part of innovative products and applications. Hands-on Machine Learning with JavaScript presents various avenues of machine learning in a practical and objective way, and helps implement them using the JavaScript language. Predicting behaviors, analyzing feelings, grouping data, and building neural models are some of the skills you will build from this book. You will learn how to train your machine learning models and work with different kinds of data. During this journey, you will come across use cases such as face detection, spam filtering, recommendation systems, character recognition, and more. Moreover, you will learn how to work with deep neural networks and guide your applications to gain insights from data. By the end of this book, you'll have gained hands-on knowledge on evaluating and implementing the right model, along with choosing from different JS libraries, such as NaturalNode, brain, harthur, classifier, and many more to design smarter applications.
Table of Contents (14 chapters)

Improvements in ES6

The ECMAScript committee, which defines the specification for the JavaScript language itself, released a new specification called ECMAScript 6/ECMAScript 2015 in June 2015. The new standard, called ES6 for short, was a major revision of the JavaScript programming language and added a number of new paradigms intended to make development of JavaScript programs easier.

While ECMAScript defines the specification for the JavaScript language, the actual implementation of the language is dependent on the browser vendors and the maintainers of the various JavaScript engines. ES6 by itself is only a guideline, and because the browser vendors each have their own timeline for implementing new language features, the JavaScript language and the JavaScript implementations diverged slightly. Features defined by ES6, such as classes, were not available in the major browsers, but developers wanted to use them anyway.

Enter Babel, the JavaScript transpiler. Babel can read and parse different JavaScript flavors (such as ES6, ES7, ES8, and React JSX) and convert it or compile it into browser-standard ES5. Even today, the entirety of ES6 has not yet been implemented by the browser vendors, so Babel remains an essential tool for developers wishing to write ES6 code.

The examples in this book will use ES6. If you're not yet familiar with the newer syntax, here are a few of the major features you'll see used throughout this book.

Let and const

In ES5 JavaScript, we use the var keyword to define variables. In most cases, var can simply be replaced with let, with the major difference between the two constructs being the scoping of the variable with respect to blocks. The following example from MDN web docs, or previously Mozilla Developer Network (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let), demonstrates the subtle difference between the two:

function varTest() {
var x = 1;
if (true) {
var x = 2; // same variable!
console.log(x); // 2
}
console.log(x); // 2
}

function letTest() {
let x = 1;
if (true) {
let x = 2; // different variable
console.log(x); // 2
}
console.log(x); // 1
}

So, while you must use additional caution in cases like the preceding one, in most cases you can simply replace var with let.

The const keyword, unlike let, defines a variable as a constant; that is, you cannot reassign a variable initialized with const at a later date. For example, the following code causes an error with a message similar to invalid assignment to const a:

const a = 1;
a = 2;

On the other hand the same code, using var or let to define a, would run successfully.

Note that if a is an object, you are allowed to modify object properties of a.

The following code will run successfully:

const obj = {};
obj.name = ‘My Object’;

However, attempting to redefine objects such as in obj = {name: “other object”} would cause an error.

I find that in most programming contexts, const is typically more appropriate than let, as most variables you use never need to be redefined. My recommendation is to use const as much as you can, and use let only when you have a reason to redefine the variable later.

Classes

One very welcome change in ES6 is the addition of classes and class inheritance. Previously, object-oriented programming in JavaScript required prototypical inheritance, which many developers found unintuitive, like the following ES5 example:

var Automobile = function(weight, speed) {
this.weight = weight;
this.speed = speed;
}
Automobile.prototype.accelerate = function(extraSpeed) {
this.speed += extraSpeed;
}
var RaceCar = function (weight, speed, boost) {
Automobile.call(this, weight, speed);
this.boost = boost;
}
RaceCar.prototype = Object.create(Automobile.prototype);
RaceCar.prototype.constructor = RaceCar;
RaceCar.prototype.accelerate = function(extraSpeed) {
this.speed += extraSpeed + this.boost;
}

In the preceding code, extending an object requires calling the parent class in the child's constructor function, creating a clone of the parent's prototype object, and overriding the parent's prototype constructor with the child's prototype constructor. These steps were seen as unintuitive and burdensome by most developers.

Using ES6 classes, however, the code will look like this:

class Automobile {
constructor(weight, speed) {
this.weight = weight;
this.speeed = speed;
}
accelerate(extraSpeed) {
this.speed += extraSpeed;
}
}
class RaceCar extends Automobile {
constructor(weight, speed, boost) {
super(weight, speed);
this.boost = boost;
}
accelerate(extraSpeed) {
this.speed += extraSpeed + this.boost;
}
}

The preceding syntax is more in line with what we'd expect from object-oriented programming, and also makes inheritance much simpler.

It's important to note that under the hood, ES6 classes still use JavaScript's prototypical inheritance paradigm. Classes are just syntactic sugar on top of the existing system, so there is no significant difference between these two approaches other than clean code.

Module imports

ES6 also defines a module import and export interface. With the older CommonJS approach, modules are exported using the modules.export construct, and modules are imported with the require(filename) function. The ES6 approach looks a little different. In one file, define and export a class, as shown in the following code:

Class Automobile {

}
export default Automobile

And in another file, import the class, as shown in the following code:

import Automobile from ‘./classes/automobile.js’;
const myCar = new Automobile();

At present, Babel compiles ES6 modules to the same format as CommonJS modules, so you can use either the ES6 modules syntax or the CommonJS modules syntax if you’re using Babel.

Arrow functions

One quirky, useful, but somewhat annoying aspect of ES5 JavaScript is its heavy use of callbacks that run asynchronously. You are probably intimately familiar with jQuery code that looks something like this:

$(“#link”).click(function() {
var $self = $(this);
doSomethingAsync(1000, function(resp) {
$self.addClass(“wasFaded”);
var processedItems = resp.map(function(item) {
return processItem(item);
});
return shipItems(processedItems);
});
});

We're forced to create a variable called $self because the original this context is lost in our inner anonymous function. We also have a lot of boilerplate and difficult-to-read code due to needing to create three separate anonymous functions.

Arrow function syntax is both syntactic sugar that helps us write anonymous functions with a shorter syntax, and also a functional update that preserves the context of this inside an arrow function.

For instance, the preceding code may be written in ES6 as follows:

$(“#link”).click(function() {
dozsSomethingAsync(1000, resp => {
$(this).addClass(“wasFaded”);
const processedItems = resp.map(item => processItem(Item));
return shipItems(processedItems);
});
});

You can see in the preceding code that we no longer need a $self variable to preserve this, and our call to .map is much simpler, no longer requiring the function keyword, parentheses, curly braces, or a return statement.

Now let's look at some equivalent functions. Let's look at the following code:

const double = function(number) {
return number * 2;
}

The preceding code would be similar to:

const double = number => number * 2;
// Is equal to:
const double = (number) => { return number * 2; }

In the aforementioned examples, we can omit the parentheses around the number parameter because the function only requires one parameter. If the function required two parameters, we would be required to add parentheses as in the next example. Additionally, if the body of our function only requires one line, we can omit the function body curly braces and omit the return statement.

Let's look at another equivalence, with multiple parameters, as shown in the following code:

const sorted = names.sort(function (a, b) {
return a.localeCompare(b);
});

The preceding code would be similar to:

const sorted = names.sort((a, b) => a.localeCompare(b));

I find that arrow functions make themselves most useful in situations like the preceding one, when you're doing data transformations, especially where using Array.map, Array.filter, Array.reduce, and Array.sort calls with straightforward function bodies. Arrow functions are less useful in jQuery because of jQuery's tendency to give you data using the this context, which you don't receive with anonymous arrow functions.

Object literals

ES6 makes some improvements to object literals. There are several improvements, but the one you'll see most is the implicit naming of object properties. In ES5 it would be as follows:

var name = ‘Burak’;
var title = ‘Author’;
var object = {name: name, title: title};

In ES6, if the property name and the variable name are the same as the preceding one, you can simplify it to the following:

const name = ‘Burak’;
const title = ‘Author’;
const object = {name, title};

Additionally, ES6 introduces the object spread operator, which simplifies shallow object merges. For instance, take a look at the following code in ES5:

function combinePreferences(userPreferences) {
var defaultPreferences = {size: ‘large’, mode: ‘view’};
return Object.assign({}, defaultPreferences, userPreferences);
}

The preceding code will create a new object from defaultPreferences, and merge in properties from userPreferences. Passing an empty object to the Object.assign instance first parameter ensures that we create a new object rather than overwriting defaultPreferences (which isn't an issue in the preceding example, but is an issue in real-life use cases).

And now, let's take a look at the same in ES6:

function combinePreferences(userPreferences) {
var defaultPreferences = {size: ‘large’, mode: ‘view’};
return {...defaultPreferences, ...userPreferences};
}

This approach does the same as the ES5 example, but is quicker and easier to read in my opinion than the Object.assign method. Developers familiar with React and Redux, for instance, often use the object spread operator when managing reducer state operations.

The for...of function

The for loops over arrays in ES5 are often achieved using the for (index in array) syntax, which looks something like this:

var items = [1, 2, 3 ];
for (var index in items) {
var item = items[index];

}

And ES6 adds the for...of syntax, which saves you a step, as you can see from the following code:

const items = [1, 2, 3 ];
for (const item of items) {

}

Promises

Promises, in one form or another, have been available in JavaScript for a while. All jQuery users are familiar with the idea. A promise is a reference to a variable that is generated asynchronously and may become available in the future.

The ES5 way of doing things, if you weren't already using some sort of third-party promise library or jQuery's deferred's, was to accept a callback function to an asynchronous method and run the callback upon successful completion, as shown in the following code:

function updateUser(user, settings, onComplete, onError) {
makeAsyncApiRequest(user, settings, function(response) {
if (response.isValid()) {
onComplete(response.getBody());
} else {
onError(response.getError())
}
});
}
updateUser(user, settings, function(body) { ... }, function(error) { ... });

In ES6, you may return a Promise which encapsulates the asynchronous request and either gets resolved or rejected, as shown in the following code:

function updateUser(user, settings) {
return new Promise((resolve, reject) => {
makeAsyncApiRequest(user, settings, function(response) {
if (response.isValid()) {
resolve(response.getBody());
} else {
reject(response.getError())
}
});
});
}
updateUser(user, settings)
.then(
body => { ... },
error => { ... }
);

The real power of promises is that they can be passed around as objects, and promise handlers can be chained.

The async/await functions

The async and await keywords are not an ES6 feature but rather an ES8 feature. While promises bring huge improvements to the way we deal with asynchronous calls, promises also are susceptible to lots of method chaining, and in some cases force us to use asynchronous paradigms when we really just want to write a function that acts asynchronously but reads as if it were a synchronous function.

Now let's take a look at the following example from MDN's asynchronous function reference page (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function):

function resolveAfter2Seconds() {
return new Promise(resolve => {
setTimeout(() => {
resolve('resolved');
}, 2000);
});
}
async function asyncCall() {
console.log('calling');
var result = await resolveAfter2Seconds();
console.log(result);
// expected output: "resolved"
}
asyncCall();

The resolveAfter2Seconds function is a normal JavaScript function that returns an ES6 promise. The magic is in the asyncCall function, which is marked by the async keyword. Inside asyncCall, we invoke resolveAfter2Seconds with the await keyword, rather than using the more familiar promise .then(result => console.log(result)) construct we'd normally use in ES6. The await keyword makes our async function wait for the promise to resolve before continuing, and returns the result of the Promise directly. In this manner, async/await can convert asynchronous functions that use promises to read like synchronous functions, which should help keep deeply nested promise calls and asynchronous function stats neat and easy to read.

The async and await features are part of ES8, not ES6, so when we set up Babel in a few minutes we'll need to be sure to include all new versions of EMCAScript in our configuration, not just ES6.