SlideShare a Scribd company logo
Clean codeClean codeAdapted in JavaScript
Clean code in JavaScript
AgendaAgenda
Variables
Functions
Classes
Miscellaneous
VariablesVariables
There are only two hard things inThere are only two hard things in
Computer Science: cache invalidationComputer Science: cache invalidation
and naming things.and naming things.
There are only two hard things inThere are only two hard things in
Computer Science: cache invalidationComputer Science: cache invalidation
and naming things.and naming things.
“ Phil Karlton
Use meaningful and pronounceable variable namesUse meaningful and pronounceable variable names
const yyyymmdstr = moment().format('YYYY/MM/DD');
const yearMonthDay = moment().format('YYYY/MM/DD');
Use the same vocabulary for the same type of variableUse the same vocabulary for the same type of variable
getUserInfo();
getClientData();
getCustomerRecord();
getUser();
Use searchable namesUse searchable names
// What the heck is 525600 for?
for (let i = 0; i < 525600; i++) {
runCronJob();
}
// Declare them as capitalized `const` globals.
const MINUTES_IN_A_YEAR = 525600;
for (let i = 0; i < MINUTES_IN_A_YEAR; i++) {
runCronJob();
}
Keep convention in mindKeep convention in mind
class AnimalSoSweet {} // UpperCamelCase
let anAnimal = new AnimalSoSweet(); // LowerCamlCase
function sendARequest(requestToSend){};// LowerCamlCase
const NB_MS_IN_HOUR = 3600; // SCREAMING_SNAKE_CASE
Use explanatory variablesUse explanatory variables
const cityStateRegex = /^(.+)[,s]+(.+?)s*(d{5})?$/;
saveCityState(
cityStateRegex.match(cityStateRegex)[1],
cityStateRegex.match(cityStateRegex)[2]
);
const cityStateRegex = /^(.+)[,s]+(.+?)s*(d{5})?$/;
const match = cityStateRegex.match(cityStateRegex)
const city = match[1];
const state = match[2];
saveCityState(city, state);
Avoid Mental MappingAvoid Mental Mapping
const locations = ['Austin', 'New York', 'San Francisco'];
locations.forEach((l) => {
doStuff();
doSomeOtherStuff();
// ...
// ...
// ...
// Wait, what is `l` for again?
dispatch(l);
});
const locations = ['Austin', 'New York', 'San Francisco'];
locations.forEach((location) => {
doStuff();
doSomeOtherStuff();
// ...
// ...
// ...
dispatch(location);
});
Don't add unneeded contextDon't add unneeded context
const Car = {
carMake: 'Honda',
carModel: 'Accord',
carColor: 'Blue'
};
function paintCar(car) {
car.carColor = 'Red';
}
const Car = {
make: 'Honda',
model: 'Accord',
color: 'Blue'
};
function paintCar(car) {
car.color = 'Red';
}
Short-circuiting is cleaner than conditionalsShort-circuiting is cleaner than conditionals
function createMicrobrewery(name) {
let breweryName;
if (name) {
breweryName = name;
} else {
breweryName = 'Hipster Brew Co.';
}
}
function createMicrobrewery(name) {
const breweryName = name || 'Hipster Brew Co.'
}
FunctionsFunctions
Function arguments (2 or fewer ideally)Function arguments (2 or fewer ideally)
function createMenu(title, body, buttonText, cancellable) {
// ...
}
const menuConfig = {
title: 'Foo',
body: 'Bar',
buttonText: 'Baz',
cancellable: true
}
function createMenu(menuConfig) {
// ...
}
Functions should do one thing (Small !)Functions should do one thing (Small !)
function emailClients(clients) {
clients.forEach(client => {
const clientRecord = database.lookup(client);
if (clientRecord.isActive()) {
email(client);
}
});
}
function emailClients(clients) {
clients
.filter(isClientActive)
.forEach(email);
}
function isClientActive(client) {
const clientRecord = database.lookup(client);
return clientRecord.isActive();
}
Don't use flags as function parametersDon't use flags as function parameters
function createFile(name, temp) {
if (temp) {
fs.create('./temp/' + name);
} else {
fs.create(name);
}
}
function createFile(name) {
fs.create(name);
}
function createTempFile(name) {
createFile('./temp/' + name);
}
Function names should say what they doFunction names should say what they do
function dateAdd(date, month) {
// ...
}
const date = new Date();
/* It's hard to to tell from the
function name what is added
*/
dateAdd(date, 1);
function dateAddMonth(date, month) {
// ...
}
const date = new Date();
dateAddMonth(date, 1);
Function should not have side effectFunction should not have side effect
let name = 'Ryan McDermott';
function splitIntoFirstAndLastName() {
name = name.split(' ');
}
splitIntoFirstAndLastName();
function splitIntoFirstAndLastName(name) {
return name.split(' ');
}
const name = 'Ryan McDermott'
const newName = splitIntoFirstAndLastName(name);
Use default arguments instead of short circuitingUse default arguments instead of short circuiting
function writeForumComment(subject, body) {
subject = subject || 'No Subject';
body = body || 'No text';
}
function writeForumComment(
subject = 'No subject',
body = 'No text') {
// ...
}
Encapsulate conditionalsEncapsulate conditionals
if (fsm.state === 'fetching' && isEmpty(listNode)) {
// ...
}
function shouldShowSpinner(fsm, listNode) {
return fsm.state === 'fetching' && isEmpty(listNode);
}
if (shouldShowSpinner(fsmInstance, listNodeInstance)) {
// ...
}
Sort correctly callers and calleeSort correctly callers and callee
function doVerySpecificThing(){
//...
}
function doSpecificThing(){
//...
doVerySpecificThing();
}
function doThing(){
//...
doSpecificThing();
}
function doThing(){
//...
doSpecificThing();
}
function doSpecificThing(){
//...
doVerySpecificThing();
}
function doVerySpecificThing(){
//...
}
Prefer functional programming over imperativePrefer functional programming over imperative
const programmerOutput = [
{ name: 'Uncle Bobby', linesOfCode: 500 },
{ name: 'Suzie Q', linesOfCode: 1500 }
//....
];
let totalOutput = 0;
for (let i = 0; i < programmerOutput.length; i++) {
totalOutput += programmerOutput[i].linesOfCode;
}
const programmerOutput = [
{ name: 'Uncle Bobby', linesOfCode: 500 },
{ name: 'Suzie Q', linesOfCode: 1500 }
//....
];
const totalOutput = programmerOutput
.map((programmer) => programmer.linesOfCode)
.reduce((acc, linesOfCode) => acc + linesOfCode, 0);
Prefer polymorphism over lot of conditionalsPrefer polymorphism over lot of conditionals
class Airplane {
// ...
getCruisingAltitude() {
switch (this.type) {
case '777':
return this.getMaxAltitude() - this.getPassengerCount();
case 'Air Force One':
return this.getMaxAltitude();
case 'Cessna':
return this.getMaxAltitude() - this.getFuelExpenditure();
}
}
}
class Airplane {}
class Boeing777 extends Airplane {
getCruisingAltitude() {
return this.getMaxAltitude() - this.getPassengerCount();
}
}
class AirForceOne extends Airplane {
getCruisingAltitude() {
return this.getMaxAltitude();
}
}
Never everNever ever lets dead or commented code !!!lets dead or commented code !!!
// function oldRequestModule(url) {
// ...
// }
function newRequestModule(url) {
// ...
}
const req = newRequestModule;
inventoryTracker('apples', req, 'www.inventory-awesome.io');
function newRequestModule(url) {
// ...
}
const req = newRequestModule;
inventoryTracker('apples', req, 'www.inventory-awesome.io');
ClassesClasses
SSingle responsibilityingle responsibility
OOpen-closedpen-closed
LLiskov substitutioniskov substitution
IInterface segregationnterface segregation
DDependency injectionependency injection
SOLIDSOLID principlesprinciples
en.wikipedia.org/wiki/SOLID_(object-oriented_design)
Single Responsibility Principle (Single Responsibility Principle (SSRP)RP)
class UserService {
saveUser(user){
eventEmmitter.dispatch(ADD_USER, user);
return db.exec('INSERT VALUES ...', user);
}
}
class UserResource {
//...
save(user){
return db.exec('INSERT VALUES ...', user);
}
}
class UserService {
//...
saveUser(user){
eventEmmitter.dispatch(ADD_USER, user);
return userRessource.save(user)
}
}
"There should never be more than one reason for a class to change"
Open/Closed Principle (Open/Closed Principle (OOCP)CP)
class ExpenseController {
constructor() {}
addNewExpense(expense) {
if (this.validateExpense(expense)){
expenseService.save(expense);
}
}
validateExpense (expense){
return !!expense.description;
}
}
class UserAuth {
constructor(user) {
this.user = user;
}
verifyCredentials() {
// ...
}
}
class ExpenseController {
constructor(validators) {this.validators=validators;}
addNewExpense(expense) {
if (this.validateExpense(expense)){
expenseService.save(expense);
}
}
validateExpense (expense){
return this.validators.every(validator => {
return validator.isValid(expense));
});
}
}
"open for extension, but closed for modification"
Liskov substitution principle (Liskov substitution principle (LLSP)SP)
class Rectangle {
//...
setWidth(width) {
this.width = width;
}
setHeight(height) {
this.height = height;
}
getArea() {
return this.width * this.height;
}
}
class Square extends Rectangle {
// ...
setWidth(width) {
this.width = width;
this.height = width;
}
setHeight(height) {
this.width = height;
this.height = height;
}
}
function renderLargeRectangles(rectangles) {
rectangles.forEach((rectangle) => {
rectangle.setWidth(4);
rectangle.setHeight(5);
const area = rectangle.getArea();//Will return 25 for Square. Should be 20.
rectangle.render(area);
});
}
const rectangles = [new Rectangle(), new Rectangle(), new Square()];
renderLargeRectangles(rectangles);
Liskov substitution principle (Liskov substitution principle (LLSP)SP)
class Shape {
setColor(color) {
// ...
}
render(area) {
// ...
}
}
class Rectangle extends Shape {
constructor() {
super();
this.width = 0;
this.height = 0;
}
setWidth(width) {
this.width = width;
}
setHeight(height) {
this.height = height;
}
getArea() {
return this.width * this.height;
}
}
class Square extends Shape {
constructor() {
super();
this.length = 0;
}
setLength(length) {
this.length = length;
}
getArea() {
return this.length * this.length;
}
}
function renderLargeShapes(shapes) {
shapes.forEach((shape) => {
switch (shape.constructor.name) {
case 'Square':
shape.setLength(5);
break;
case 'Rectangle':
shape.setWidth(4);
shape.setHeight(5);
}
const area = shape.getArea();
shape.render(area);
});
}
Interface segregation principle (Interface segregation principle (IISP)SP)
class ACar {
run(){
throw new Error('SubType of car should implement run');
}
fillFuel(){
throw new Error('SubType of car should implement fillFuel');
}
}
class MyOldCar extends ACar{
run(){
// run ...
}
fillFuel(){
// handle fillFuel
}
}
class MyElectricCar extends ACar{
run(){
// run ...
}
fillFuel(){
// Not necessary
}
}
"Clients should not be forced to depend upon interfaces that they do not use."
Interface segregation principle (Interface segregation principle (IISP)SP)
class ACar {
run(){
throw new Error('SubType of car should implement run');
}
}
const FuelCar = (Superclass) => class extends Superclass {
fillFuel() {
throw new Error('SubType of FuelCar should implement fillFuel');
}
}
class MyOldCar extends FuelCar(ACar){
run(){
// run ...
}
fillFuel(){
// handle fillFuel
}
}
const ElectricCar = (Superclass) => class extends Superclass {
reload() {
throw new Error('SubType of ElectricCar should implement reload');
}
}
class MyElectricCar extends ElectricCar(ACar){
run(){
// run ...
}
reload(){
// handle reload
}
}
"Clients should not be forced to depend upon interfaces that they do not use."
Dependency Inversion Principle (Dependency Inversion Principle (DDIP)IP)
"1. High-level modules should not depend on low-level modules. Both should depend on abstractions.
2. Abstractions should not depend upon details. Details should depend on abstractions."
class UserController {
constructor(){
this.userService = new UserService();
}
save(user){
this.userService.save(user);
}
}
class UserController {
constructor(userService){
this.userService = userService;
}
save(user){
this.userService.save(user);
}
}
Use method chainingUse method chaining
"1. High-level modules should not depend on low-level modules. Both should depend on abstractions.
2. Abstractions should not depend upon details. Details should depend on abstractions."
class User {
constructor(name, age){
this.name = name;
this.age = age;
}
setName(name){
this.name = name;
}
setAge(age){
this.age = age;
}
}
const user = new User();
user.setName('Mathieu');
user.setAge('28');
class User {
constructor(name, age){
this.name = name;
this.age = age;
}
setName(name){
this.name = name;
return this;
}
setAge(age){
this.age = age;
return this;
}
}
const user = new User()
.setName('Mathieu')
.setAge('28');
MiscellaneousMiscellaneous   
Only comment things that have business logic complexity.Only comment things that have business logic complexity.
function hashIt(data) {
// The hash
let hash = 0;
// Length of string
const length = data.length;
// Loop through every character in data
for (let i = 0; i < length; i++) {
// Get character code.
const char = data.charCodeAt(i);
// Make the hash
hash = ((hash << 5) - hash) + char;
// Convert to 32-bit integer
hash &= hash;
}
}
"Comments are an apology, not a requirement. Good code mostly documents itself."
function hashIt(data) {
let hash = 0;
const length = data.length;
for (let i = 0; i < length; i++) {
const char = data.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
// Convert to 32-bit integer
hash &= hash;
}
}
Bad commentsBad comments
/**
* 2016-12-20: Removed monads, didn't understand them (RM)
* 2016-10-01: Improved using special monads (JP)
* 2016-02-03: Removed type-checking (LI)
* 2015-03-14: Added combine with type-checking (JR)
*/
////////////////////////////////////////////////////////////////////////////////
// Scope Model Instantiation
////////////////////////////////////////////////////////////////////////////////
$scope.model = {
menu: 'foo',
nav: 'bar'
};
while (toto < MAX_VALUES){
if (test > MIN_VALUES){
} // if
} // while
FollowFollow
the cleanthe clean
code rulescode rules
slides.com/mbreton/clean-code-javascriptslides.com/mbreton/clean-code-javascript
github.com/ryanmcdermott/clean-code-javascriptgithub.com/ryanmcdermott/clean-code-javascript

More Related Content

PPTX
The Single Responsibility Principle
PPTX
Mobile application development ppt
PDF
Introduction to React Native
PPTX
Diabetes Mellitus
PPTX
Hypertension
PPTX
Republic Act No. 11313 Safe Spaces Act (Bawal Bastos Law).pptx
PPTX
Power Point Presentation on Artificial Intelligence
The Single Responsibility Principle
Mobile application development ppt
Introduction to React Native
Diabetes Mellitus
Hypertension
Republic Act No. 11313 Safe Spaces Act (Bawal Bastos Law).pptx
Power Point Presentation on Artificial Intelligence

What's hot (20)

PPTX
Clean code slide
PDF
PDF
Clean Code JavaScript
PDF
Angular Directives
PDF
JavaScript - Chapter 8 - Objects
ODP
Clean code
PPTX
Naming Standards, Clean Code
PPTX
Clean code
PPTX
Clean Code
PDF
Polymorphism in Java by Animesh Sarkar
PPTX
Javascript
PPTX
6. static keyword
PPTX
Sharing Data Between Angular Components
PDF
Java IO
PPTX
Clean code
PPTX
Constructor overloading & method overloading
PPTX
Angular Data Binding
PPT
Php Error Handling
PDF
Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 1
PPTX
Java 8 presentation
Clean code slide
Clean Code JavaScript
Angular Directives
JavaScript - Chapter 8 - Objects
Clean code
Naming Standards, Clean Code
Clean code
Clean Code
Polymorphism in Java by Animesh Sarkar
Javascript
6. static keyword
Sharing Data Between Angular Components
Java IO
Clean code
Constructor overloading & method overloading
Angular Data Binding
Php Error Handling
Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 1
Java 8 presentation
Ad

Similar to Clean code in JavaScript (20)

PDF
Clean code
PPTX
ES6 Overview
PPTX
Pro typescript.ch03.Object Orientation in TypeScript
PDF
Hello Swift Final 5/5 - Structures and Classes
PPTX
Architecting JavaScript Code
PDF
Say It With Javascript
PPT
Wakanday JS201 Best Practices
KEY
Symfony2 Building on Alpha / Beta technology
PDF
JavaScript Core
PDF
JavaScript - Agora nervoso
PDF
Applying Real-time SQL Changes in your Hazelcast Data Grid
KEY
Object-Oriented JavaScript
KEY
Object-Oriented Javascript
PPTX
Thinking Functionally with JavaScript
PPTX
AST - the only true tool for building JavaScript
PDF
JavaScript and UI Architecture Best Practices
PDF
Building Lithium Apps
PDF
Doctrine For Beginners
PDF
Dependency Management with RequireJS
PDF
From android/ java to swift (2)
Clean code
ES6 Overview
Pro typescript.ch03.Object Orientation in TypeScript
Hello Swift Final 5/5 - Structures and Classes
Architecting JavaScript Code
Say It With Javascript
Wakanday JS201 Best Practices
Symfony2 Building on Alpha / Beta technology
JavaScript Core
JavaScript - Agora nervoso
Applying Real-time SQL Changes in your Hazelcast Data Grid
Object-Oriented JavaScript
Object-Oriented Javascript
Thinking Functionally with JavaScript
AST - the only true tool for building JavaScript
JavaScript and UI Architecture Best Practices
Building Lithium Apps
Doctrine For Beginners
Dependency Management with RequireJS
From android/ java to swift (2)
Ad

More from Mathieu Breton (8)

PDF
TDD in Javascript
PDF
Meet VueJs
PDF
FalcorJS
PDF
BDD in Javascript
PDF
NodeJS Spring style Inversifyjs
PDF
Rollup.js
PDF
Présentation de Dart
PDF
JavaScript the-next-big...bytecode
TDD in Javascript
Meet VueJs
FalcorJS
BDD in Javascript
NodeJS Spring style Inversifyjs
Rollup.js
Présentation de Dart
JavaScript the-next-big...bytecode

Recently uploaded (20)

PPTX
MYSQL Presentation for SQL database connectivity
PPTX
Cloud computing and distributed systems.
PPTX
Big Data Technologies - Introduction.pptx
PDF
Advanced IT Governance
PDF
Chapter 2 Digital Image Fundamentals.pdf
PDF
CIFDAQ's Market Wrap: Ethereum Leads, Bitcoin Lags, Institutions Shift
PDF
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
PDF
Electronic commerce courselecture one. Pdf
PPTX
Understanding_Digital_Forensics_Presentation.pptx
PDF
The Rise and Fall of 3GPP – Time for a Sabbatical?
PDF
How Onsite IT Support Drives Business Efficiency, Security, and Growth.pdf
PDF
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
PDF
HCSP-Presales-Campus Network Planning and Design V1.0 Training Material-Witho...
PDF
KodekX | Application Modernization Development
PPTX
Telecom Fraud Prevention Guide | Hyperlink InfoSystem
PDF
NewMind AI Weekly Chronicles - August'25 Week I
PDF
Advanced Soft Computing BINUS July 2025.pdf
PPTX
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
PDF
Transforming Manufacturing operations through Intelligent Integrations
PDF
Shreyas Phanse Resume: Experienced Backend Engineer | Java • Spring Boot • Ka...
MYSQL Presentation for SQL database connectivity
Cloud computing and distributed systems.
Big Data Technologies - Introduction.pptx
Advanced IT Governance
Chapter 2 Digital Image Fundamentals.pdf
CIFDAQ's Market Wrap: Ethereum Leads, Bitcoin Lags, Institutions Shift
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
Electronic commerce courselecture one. Pdf
Understanding_Digital_Forensics_Presentation.pptx
The Rise and Fall of 3GPP – Time for a Sabbatical?
How Onsite IT Support Drives Business Efficiency, Security, and Growth.pdf
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
HCSP-Presales-Campus Network Planning and Design V1.0 Training Material-Witho...
KodekX | Application Modernization Development
Telecom Fraud Prevention Guide | Hyperlink InfoSystem
NewMind AI Weekly Chronicles - August'25 Week I
Advanced Soft Computing BINUS July 2025.pdf
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
Transforming Manufacturing operations through Intelligent Integrations
Shreyas Phanse Resume: Experienced Backend Engineer | Java • Spring Boot • Ka...

Clean code in JavaScript

  • 5. There are only two hard things inThere are only two hard things in Computer Science: cache invalidationComputer Science: cache invalidation and naming things.and naming things. There are only two hard things inThere are only two hard things in Computer Science: cache invalidationComputer Science: cache invalidation and naming things.and naming things. “ Phil Karlton
  • 6. Use meaningful and pronounceable variable namesUse meaningful and pronounceable variable names const yyyymmdstr = moment().format('YYYY/MM/DD'); const yearMonthDay = moment().format('YYYY/MM/DD');
  • 7. Use the same vocabulary for the same type of variableUse the same vocabulary for the same type of variable getUserInfo(); getClientData(); getCustomerRecord(); getUser();
  • 8. Use searchable namesUse searchable names // What the heck is 525600 for? for (let i = 0; i < 525600; i++) { runCronJob(); } // Declare them as capitalized `const` globals. const MINUTES_IN_A_YEAR = 525600; for (let i = 0; i < MINUTES_IN_A_YEAR; i++) { runCronJob(); }
  • 9. Keep convention in mindKeep convention in mind class AnimalSoSweet {} // UpperCamelCase let anAnimal = new AnimalSoSweet(); // LowerCamlCase function sendARequest(requestToSend){};// LowerCamlCase const NB_MS_IN_HOUR = 3600; // SCREAMING_SNAKE_CASE
  • 10. Use explanatory variablesUse explanatory variables const cityStateRegex = /^(.+)[,s]+(.+?)s*(d{5})?$/; saveCityState( cityStateRegex.match(cityStateRegex)[1], cityStateRegex.match(cityStateRegex)[2] ); const cityStateRegex = /^(.+)[,s]+(.+?)s*(d{5})?$/; const match = cityStateRegex.match(cityStateRegex) const city = match[1]; const state = match[2]; saveCityState(city, state);
  • 11. Avoid Mental MappingAvoid Mental Mapping const locations = ['Austin', 'New York', 'San Francisco']; locations.forEach((l) => { doStuff(); doSomeOtherStuff(); // ... // ... // ... // Wait, what is `l` for again? dispatch(l); }); const locations = ['Austin', 'New York', 'San Francisco']; locations.forEach((location) => { doStuff(); doSomeOtherStuff(); // ... // ... // ... dispatch(location); });
  • 12. Don't add unneeded contextDon't add unneeded context const Car = { carMake: 'Honda', carModel: 'Accord', carColor: 'Blue' }; function paintCar(car) { car.carColor = 'Red'; } const Car = { make: 'Honda', model: 'Accord', color: 'Blue' }; function paintCar(car) { car.color = 'Red'; }
  • 13. Short-circuiting is cleaner than conditionalsShort-circuiting is cleaner than conditionals function createMicrobrewery(name) { let breweryName; if (name) { breweryName = name; } else { breweryName = 'Hipster Brew Co.'; } } function createMicrobrewery(name) { const breweryName = name || 'Hipster Brew Co.' }
  • 15. Function arguments (2 or fewer ideally)Function arguments (2 or fewer ideally) function createMenu(title, body, buttonText, cancellable) { // ... } const menuConfig = { title: 'Foo', body: 'Bar', buttonText: 'Baz', cancellable: true } function createMenu(menuConfig) { // ... }
  • 16. Functions should do one thing (Small !)Functions should do one thing (Small !) function emailClients(clients) { clients.forEach(client => { const clientRecord = database.lookup(client); if (clientRecord.isActive()) { email(client); } }); } function emailClients(clients) { clients .filter(isClientActive) .forEach(email); } function isClientActive(client) { const clientRecord = database.lookup(client); return clientRecord.isActive(); }
  • 17. Don't use flags as function parametersDon't use flags as function parameters function createFile(name, temp) { if (temp) { fs.create('./temp/' + name); } else { fs.create(name); } } function createFile(name) { fs.create(name); } function createTempFile(name) { createFile('./temp/' + name); }
  • 18. Function names should say what they doFunction names should say what they do function dateAdd(date, month) { // ... } const date = new Date(); /* It's hard to to tell from the function name what is added */ dateAdd(date, 1); function dateAddMonth(date, month) { // ... } const date = new Date(); dateAddMonth(date, 1);
  • 19. Function should not have side effectFunction should not have side effect let name = 'Ryan McDermott'; function splitIntoFirstAndLastName() { name = name.split(' '); } splitIntoFirstAndLastName(); function splitIntoFirstAndLastName(name) { return name.split(' '); } const name = 'Ryan McDermott' const newName = splitIntoFirstAndLastName(name);
  • 20. Use default arguments instead of short circuitingUse default arguments instead of short circuiting function writeForumComment(subject, body) { subject = subject || 'No Subject'; body = body || 'No text'; } function writeForumComment( subject = 'No subject', body = 'No text') { // ... }
  • 21. Encapsulate conditionalsEncapsulate conditionals if (fsm.state === 'fetching' && isEmpty(listNode)) { // ... } function shouldShowSpinner(fsm, listNode) { return fsm.state === 'fetching' && isEmpty(listNode); } if (shouldShowSpinner(fsmInstance, listNodeInstance)) { // ... }
  • 22. Sort correctly callers and calleeSort correctly callers and callee function doVerySpecificThing(){ //... } function doSpecificThing(){ //... doVerySpecificThing(); } function doThing(){ //... doSpecificThing(); } function doThing(){ //... doSpecificThing(); } function doSpecificThing(){ //... doVerySpecificThing(); } function doVerySpecificThing(){ //... }
  • 23. Prefer functional programming over imperativePrefer functional programming over imperative const programmerOutput = [ { name: 'Uncle Bobby', linesOfCode: 500 }, { name: 'Suzie Q', linesOfCode: 1500 } //.... ]; let totalOutput = 0; for (let i = 0; i < programmerOutput.length; i++) { totalOutput += programmerOutput[i].linesOfCode; } const programmerOutput = [ { name: 'Uncle Bobby', linesOfCode: 500 }, { name: 'Suzie Q', linesOfCode: 1500 } //.... ]; const totalOutput = programmerOutput .map((programmer) => programmer.linesOfCode) .reduce((acc, linesOfCode) => acc + linesOfCode, 0);
  • 24. Prefer polymorphism over lot of conditionalsPrefer polymorphism over lot of conditionals class Airplane { // ... getCruisingAltitude() { switch (this.type) { case '777': return this.getMaxAltitude() - this.getPassengerCount(); case 'Air Force One': return this.getMaxAltitude(); case 'Cessna': return this.getMaxAltitude() - this.getFuelExpenditure(); } } } class Airplane {} class Boeing777 extends Airplane { getCruisingAltitude() { return this.getMaxAltitude() - this.getPassengerCount(); } } class AirForceOne extends Airplane { getCruisingAltitude() { return this.getMaxAltitude(); } }
  • 25. Never everNever ever lets dead or commented code !!!lets dead or commented code !!! // function oldRequestModule(url) { // ... // } function newRequestModule(url) { // ... } const req = newRequestModule; inventoryTracker('apples', req, 'www.inventory-awesome.io'); function newRequestModule(url) { // ... } const req = newRequestModule; inventoryTracker('apples', req, 'www.inventory-awesome.io');
  • 27. SSingle responsibilityingle responsibility OOpen-closedpen-closed LLiskov substitutioniskov substitution IInterface segregationnterface segregation DDependency injectionependency injection SOLIDSOLID principlesprinciples en.wikipedia.org/wiki/SOLID_(object-oriented_design)
  • 28. Single Responsibility Principle (Single Responsibility Principle (SSRP)RP) class UserService { saveUser(user){ eventEmmitter.dispatch(ADD_USER, user); return db.exec('INSERT VALUES ...', user); } } class UserResource { //... save(user){ return db.exec('INSERT VALUES ...', user); } } class UserService { //... saveUser(user){ eventEmmitter.dispatch(ADD_USER, user); return userRessource.save(user) } } "There should never be more than one reason for a class to change"
  • 29. Open/Closed Principle (Open/Closed Principle (OOCP)CP) class ExpenseController { constructor() {} addNewExpense(expense) { if (this.validateExpense(expense)){ expenseService.save(expense); } } validateExpense (expense){ return !!expense.description; } } class UserAuth { constructor(user) { this.user = user; } verifyCredentials() { // ... } } class ExpenseController { constructor(validators) {this.validators=validators;} addNewExpense(expense) { if (this.validateExpense(expense)){ expenseService.save(expense); } } validateExpense (expense){ return this.validators.every(validator => { return validator.isValid(expense)); }); } } "open for extension, but closed for modification"
  • 30. Liskov substitution principle (Liskov substitution principle (LLSP)SP) class Rectangle { //... setWidth(width) { this.width = width; } setHeight(height) { this.height = height; } getArea() { return this.width * this.height; } } class Square extends Rectangle { // ... setWidth(width) { this.width = width; this.height = width; } setHeight(height) { this.width = height; this.height = height; } } function renderLargeRectangles(rectangles) { rectangles.forEach((rectangle) => { rectangle.setWidth(4); rectangle.setHeight(5); const area = rectangle.getArea();//Will return 25 for Square. Should be 20. rectangle.render(area); }); } const rectangles = [new Rectangle(), new Rectangle(), new Square()]; renderLargeRectangles(rectangles);
  • 31. Liskov substitution principle (Liskov substitution principle (LLSP)SP) class Shape { setColor(color) { // ... } render(area) { // ... } } class Rectangle extends Shape { constructor() { super(); this.width = 0; this.height = 0; } setWidth(width) { this.width = width; } setHeight(height) { this.height = height; } getArea() { return this.width * this.height; } } class Square extends Shape { constructor() { super(); this.length = 0; } setLength(length) { this.length = length; } getArea() { return this.length * this.length; } } function renderLargeShapes(shapes) { shapes.forEach((shape) => { switch (shape.constructor.name) { case 'Square': shape.setLength(5); break; case 'Rectangle': shape.setWidth(4); shape.setHeight(5); } const area = shape.getArea(); shape.render(area); }); }
  • 32. Interface segregation principle (Interface segregation principle (IISP)SP) class ACar { run(){ throw new Error('SubType of car should implement run'); } fillFuel(){ throw new Error('SubType of car should implement fillFuel'); } } class MyOldCar extends ACar{ run(){ // run ... } fillFuel(){ // handle fillFuel } } class MyElectricCar extends ACar{ run(){ // run ... } fillFuel(){ // Not necessary } } "Clients should not be forced to depend upon interfaces that they do not use."
  • 33. Interface segregation principle (Interface segregation principle (IISP)SP) class ACar { run(){ throw new Error('SubType of car should implement run'); } } const FuelCar = (Superclass) => class extends Superclass { fillFuel() { throw new Error('SubType of FuelCar should implement fillFuel'); } } class MyOldCar extends FuelCar(ACar){ run(){ // run ... } fillFuel(){ // handle fillFuel } } const ElectricCar = (Superclass) => class extends Superclass { reload() { throw new Error('SubType of ElectricCar should implement reload'); } } class MyElectricCar extends ElectricCar(ACar){ run(){ // run ... } reload(){ // handle reload } } "Clients should not be forced to depend upon interfaces that they do not use."
  • 34. Dependency Inversion Principle (Dependency Inversion Principle (DDIP)IP) "1. High-level modules should not depend on low-level modules. Both should depend on abstractions. 2. Abstractions should not depend upon details. Details should depend on abstractions." class UserController { constructor(){ this.userService = new UserService(); } save(user){ this.userService.save(user); } } class UserController { constructor(userService){ this.userService = userService; } save(user){ this.userService.save(user); } }
  • 35. Use method chainingUse method chaining "1. High-level modules should not depend on low-level modules. Both should depend on abstractions. 2. Abstractions should not depend upon details. Details should depend on abstractions." class User { constructor(name, age){ this.name = name; this.age = age; } setName(name){ this.name = name; } setAge(age){ this.age = age; } } const user = new User(); user.setName('Mathieu'); user.setAge('28'); class User { constructor(name, age){ this.name = name; this.age = age; } setName(name){ this.name = name; return this; } setAge(age){ this.age = age; return this; } } const user = new User() .setName('Mathieu') .setAge('28');
  • 37. Only comment things that have business logic complexity.Only comment things that have business logic complexity. function hashIt(data) { // The hash let hash = 0; // Length of string const length = data.length; // Loop through every character in data for (let i = 0; i < length; i++) { // Get character code. const char = data.charCodeAt(i); // Make the hash hash = ((hash << 5) - hash) + char; // Convert to 32-bit integer hash &= hash; } } "Comments are an apology, not a requirement. Good code mostly documents itself." function hashIt(data) { let hash = 0; const length = data.length; for (let i = 0; i < length; i++) { const char = data.charCodeAt(i); hash = ((hash << 5) - hash) + char; // Convert to 32-bit integer hash &= hash; } }
  • 38. Bad commentsBad comments /** * 2016-12-20: Removed monads, didn't understand them (RM) * 2016-10-01: Improved using special monads (JP) * 2016-02-03: Removed type-checking (LI) * 2015-03-14: Added combine with type-checking (JR) */ //////////////////////////////////////////////////////////////////////////////// // Scope Model Instantiation //////////////////////////////////////////////////////////////////////////////// $scope.model = { menu: 'foo', nav: 'bar' }; while (toto < MAX_VALUES){ if (test > MIN_VALUES){ } // if } // while