Clean Code in Javascript

Clean Code - Robert C. Martin

Main focuses

  • Default values
  • Number of arguments
  • Function splitting
  • Object creation
  • Immutability
  • Global functions
  • typeof vs Typescript
  • prototype vs High order functions
  • prototype vs ES6 class
  • Concurrency

Default values

```js const createMicrobrewery = (name) => { const breweryName = name || "Hipster Brew Co."; // ... } ```

Default values

```js const createMicrobrewery = (name = "Hipster Brew Co.") => { // ... } ```

Number of arguments

```js const createMenu = (title, body, buttonText, cancellable) => { // ... } createMenu("Foo", "Bar", "Baz", true); ```

Number of arguments

```js const createMenu = ({ title, body, buttonText, cancellable }) => { // ... } createMenu({ title: "Foo", body: "Bar", buttonText: "Baz", cancellable: true }); ```

Function splitting

```js const emailClients = (clients) => { clients.forEach(client => { const clientRecord = database.lookup(client); if (clientRecord.isActive()) { email(client); } }); } ```

Function splitting

```js const emailActiveClients = (clients) => { clients.filter(isActiveClient).forEach(email); } const isActiveClient = (client) => { const clientRecord = database.lookup(client); return clientRecord.isActive(); } ```

Object creation

```js const menuConfig = { title: null, body: "Bar", cancellable: true }; const createMenu = (config) => { config.title = config.title || "Foo"; config.body = config.body || "Bar"; config.cancellable = config.cancellable !== undefined ? config.cancellable : true; } createMenu(menuConfig); ```

Object creation

```js const menuConfig = { title: "Order", // User did not include 'body' key cancellable: true }; const createMenu = (config) => { const finalConfig = Object.assign({ title: "Foo", body: "Bar", cancellable: true }, config); return finalConfig; } createMenu(menuConfig); ```

Immutability

```js const addItemToCart = (cart, item) => { cart.push({ item, date: Date.now() }); }; ```

Immutability

```js const addItemToCart = (cart, item) => { return [...cart, { item, date: Date.now() }]; }; ```

Global functions

```js Array.prototype.diff = function diff(comparisonArray) { const hash = new Set(comparisonArray); return this.filter(elem => !hash.has(elem)); }; ```

Global functions

```js class SuperArray extends Array { diff(comparisonArray) { const hash = new Set(comparisonArray); return this.filter(elem => !hash.has(elem)); } } ```

typeof vs Typescript

```js function combine(val1, val2) { if ( (typeof val1 === "number" && typeof val2 === "number") || (typeof val1 === "string" && typeof val2 === "string") ) { return val1 + val2; } throw new Error("Must be of type String or Number"); } ```

typeof vs Typescript

```js function combine(val1: number | string, val2: number | string) { return val1 + val2; } ```

prototype vs High order functions

```js const Employee = (name) => { this.name = name; }; Employee.prototype.getName = function getName() { return this.name; }; const employee = new Employee("John Doe"); console.log(`Employee name: ${employee.getName()}`); // Employee name: John Doe delete employee.name; console.log(`Employee name: ${employee.getName()}`); // Employee name: undefined ```

prototype vs High order functions

```js const makeEmployee = (name) => { return { getName() { return name; } }; } const employee = makeEmployee("John Doe"); console.log(`Employee name: ${employee.getName()}`); // Employee name: John Doe delete employee.name; console.log(`Employee name: ${employee.getName()}`); // Employee name: John Doe ```

prototype vs ES6 class

```js const Animal = (age) => { if (!(this instanceof Animal)) { throw new Error("Instantiate Animal with `new`"); } this.age = age; }; Animal.prototype.move = function move() {}; const Mammal = (age, furColor) => { if (!(this instanceof Mammal)) { throw new Error("Instantiate Mammal with `new`"); } Animal.call(this, age); this.furColor = furColor; }; Mammal.prototype = Object.create(Animal.prototype); Mammal.prototype.constructor = Mammal; Mammal.prototype.liveBirth = function liveBirth() {}; const Human = (age, furColor, languageSpoken) => { if (!(this instanceof Human)) { throw new Error("Instantiate Human with `new`"); } Mammal.call(this, age, furColor); this.languageSpoken = languageSpoken; }; Human.prototype = Object.create(Mammal.prototype); Human.prototype.constructor = Human; Human.prototype.speak = function speak() {}; ```

prototype vs ES6 class

```js class Animal { constructor(age) { this.age = age; } move() { /* ... */ } } class Mammal extends Animal { constructor(age, furColor) { super(age); this.furColor = furColor; } liveBirth() { /* ... */ } } class Human extends Mammal { constructor(age, furColor, languageSpoken) { super(age, furColor); this.languageSpoken = languageSpoken; } speak() { /* ... */ } } ```

Concurrency

```js get("https://en.wikipedia.org/wiki/Robert_Cecil_Martin", (requestErr, response, body) => { if (requestErr) { console.error(requestErr); } else { writeFile("article.html", body, writeErr => { if (writeErr) { console.error(writeErr); } else { console.log("File written"); } }); } }); ```

Concurrency

```js get("https://en.wikipedia.org/wiki/Robert_Cecil_Martin").then(body => { return writeFile("article.html", body); }).then(() => { console.log("File written"); }).catch(err => { console.error(err); }); ```

Concurrency

```js async function getCleanCodeArticle() { try { const body = await get("https://en.wikipedia.org/wiki/Robert_Cecil_Martin"); await writeFile("article.html", body); console.log("File written"); } catch (err) { console.error(err); } } getCleanCodeArticle() ```

Links

Questions