11
12
13
14
15
16
code
smell
17
18
19
20
21
var, let const
1 var variable = 5;
2 {
3 console.log('inside', variable); // 5
4 var variable = 10;
5 }
6
7 console.log('outside', variable); // 10
8 variable = variable * 2;
9 console.log('changed', variable); // 20
22
1 let variable = 5;
2
3 {
4 console.log('inside', variable); // error
5 let variable = 10;
6 }
7
8 console.log('outside', variable); // 5
9 variable = variable * 2;
10 console.log('changed', variable); // 10
1 const variable = 5;
2 variable = variable*2; // error
3 console.log('changed', variable); // doesn't get here
23
1 //bad
2 const yyyymmdstr = moment().format('YYYY/MM/DD');
3
4 //better
5 const currentDate = moment().format('YYYY/MM/DD');
1 //bad
2 class AbstractUser(){...}
3
4 //better
5 class User(){...}
24
1 //bad
2 getUserInfo();
3 getClientData();
4 getCustomerRecord();
5
6 //better
7 getUser()
25
1 //bad
2 const fruit = ['manzana', 'platano', 'fresa'];
3 // regular
4 const fruitList = ['manzana', 'platano', 'fresa'];
5 // good
6 const fruits = ['manzana', 'platano', 'fresa'];
7 // better
8 const fruitNames = ['manzana', 'platano', 'fresa'];
1 //bad
2 const open = true;
3 const write = true;
4 const fruit = true;
5
6 // good
7 const isOpen = true;
8 const canWrite = true;
9 const hasFruit = true;
26
1 //bad
2 const fruits = 3;
3
4 //better
5 const maxFruits = 5;
6 const minFruits = 1;
7 const totalFruits = 3;
1 //bad
2 createUserIfNotExists()
3 updateUserIfNotEmpty()
4 sendEmailIfFieldsValid()
5
6 //better
7 createUser(...)
8 updateUser(...)
9 sendEmail()
27
1 getUser()
2 setUser(...)
3 isValidUser()
Get set
1 class Person {
2 constructor(name) {
3 this._name = name;
4 }
5
6 get name() {
7 return this._name;
8 }
9
10 set name(newName) {
11 this._name = newName;
12 }
13 }
14
15 let person = new Person(‘Miguel’);
16 console.log(person.name); // Outputs ‘Miguel’
28
scope
1 let greeting = ‘hello world!’;
2
3 function greet(){
4 console.log(greeting);
5 }
6
7 greet(); //”Hello world”;
29
1 {
2 let greeting = “Hello world!”;
3 var lang = “English”;
4 console.log(greeting); //Hello world!
5 }
6
7 console.log(lang);//”English”
8 console.log(greeting);//// Uncaught ReferenceError: greeting is not def\
9 ined
lexical
scope
1 const number = 10;
2 function printNumber() {
3 console.log(number);
4 }
5
6 function app() {
7 const number = 5;
8 printNumber();
9 }
10
11 app(); //10
30
Hoisting
1 greet(); //”Hello world”;
2 function greet(){
3 let greeting = ‘Hello world!’;
4 console.log(greeting);
5 }
1 function greet(){
2 let greeting = ‘Hello world!’;
3 console.log(greeting);
4 }
5 greet(); //”Hello world”;
31
1 var greet = "Hi";
2 (function () {
3 console.log(greet);// "undefined"
4 var greet = "Hello";
5 console.log(greet); //”Hello”
6 })();
1 var greet = "Hi";
2 (function () {
3 var greet;
4 console.log(greet);// "undefined"
5 greet = "Hello";
6 console.log(greet); //”Hello”
7 })();
32
1 function doSomething(){
2 return "Doing something";
3 }
4
5 doSomething() //"Doing something"
33
1 const doSomething = function(){
2 return "Doing something";
3 }
4
5 doSomething() //"Doing something"
arrow
functions
1 const doSomething =() => "Doing something";
2 //El return está implícito si no añadimos las llaves.
1 //Sin arrows functions
2 [1, 2, 3].map(function(n){return n * 2})
3 //Con arrows functions
4 [1, 2, 3].map(n => n * 2)�
34
1 //Sin arrow functions
2 function add(x){
3 return function(y){
4 return x + y;
5 }
6 }
7 //con arrow functions
8 const add = x => y => x + y;
9
10 const addTwo = add(2);
11 console.log(addTwo(5))//7
this arrow functions
1 const isWindow = () => this === window;
2 isWindow(); // true
35
1 const counter = {
2 number: 0,
3 increase() {
4 setInterval(() => ++this.number, 1000);
5 }
6 };
7
8 counter.increment(); //1 2 3 4 5
1 const counter = {
2 number: 0,
3 increase() {
4 setInterval(function(){ ++this.number}, 1000);
5 }
6 };
7
8 counter.increase(); //NaN NaN NaN ...
36
1 const counter = {
2 number: 0,
3 increase() {
4 const that = this;
5 setInterval(function(){ ++that.number}, 1000);
6 }
7 };
8
9 counter.increment(); //1 2 3 ...
1 const counter = {
2 number: 0,
3 increase() {
4 setInterval(function(){ ++this.number}.bind(this), 1000);
5 }
6 };
7
8 counter.increment();
37
1 (function(){
2 // … do something
3 })()
1 (function() {
2 var number = 42;
3 }());
4
5 console.log(number); // ReferenceError
1 {
2 let number = 42;
3 }
4
5 console.log(number); // ReferenceError
38
1 const double = x => x * 2; // x es el parámetro de nuestra función
2 double(2); // 2 es el argumento con el que llamamos a nuestra función
1 function createMenu(title, body, buttonText, cancellable) {
2 // ...
3 }
4
5 function createMenu({ title, body, buttonText, cancellable }) {
6 // ...
7 }
8
9 createMenu({
10 title: 'Foo',
11 body: 'Bar',
12 buttonText: 'Baz',
13 cancellable: true
14 });
15 {title="Parámetros y argumentos", lang=javascript}
39
1 const numbers = [1, 2, 3, 4, 5];
2 numbers.map(n => n * 2); // El argumento de map es una expresión lambda.
1 //Con ES6
2 function greet(text = 'world') {
3 console.log('Hello ' + text);
4 }
5 greet(); // sin parámetro. Hello world
6 greet(undefined); // undefined. Hello world
7 greet(‘crafter’) // con parámetro. Hello crafter
1 //Antes de ES6
2 function greet(text){
3 if(typeof text === 'undefined')
4 text = ‘world’;
5
6 console.log('Hello ' + text);
7 }
40
rest spread
rest
spread
1 function add(x, y) {
2 return x + y;
3 }
4
5 add(1, 2, 3, 4, 5) //3
6
7 function add(...args){
8 return args.reduce((previous, current)=> previous + current, 0)
9 }
10
11 add(1, 2, 3, 4, 5) //15
12
13 //El parámetro rest es el último parámetro de la función y, como hemos \
14 mencionado, se trata de un array:
15
16 function process(x, y, ...args){
17 console.log(args)
18 }
19 process(1, 2, 3, 4, 5);//[3, 4, 5]
41
1 function process(x, y){
2 console.log(arguments)
3 }
4
5 process(1, 2, 3, 4, 5);//[1, 2, 3, 4, 5]
1 function doStuff (x, y, z) { }
2 const args = [0, 1, 2];
3 //con spread
4 doStuff(...args);
5
6 //sin spread
7 doStuff.apply(null, args);
8
9 //spread en funciones de math
10 const numbers = [9, 4, 7, 1];
11 Math.min(...numbers);//1
42
1 const post = {title: "Operador spread", content:"lorem ipsum…"}
2 //clonado con Object.assign();
3 const postCloned = Object.assign({}, book);
4 //clonado con el spread operator
5 const postCloned = { ...book };
6
7 const myArray = [1, 2, 3];
8 //clonado con slice()
9 const myArrayCloned = myArray.slice();
10 //clonado con el spread operator()
11 const myArrayCloned = [ ...myArray ];
1 const arrayOne = [1, 2, 3];
2 const arrayTwo = [4, 5, 6];
3
4 //concatenación con concat()
5 const myArray = arrayOne.concat(arrayTwo); //[1, 2, 3, 4, 5, 6]
6
7 //concatenacion con spread operator
8 const myArray = [...arrayOne, ...ArrayTwo]; //[1, 2, 3, 4, 5, 6]
43
1 const getPayAmount = () => {
2 let result;
3 if (isDead){
4 result = deadAmount();
5 }
6 else {
7 if (isSeparated){
8 result = separatedAmount();
9 }
10 else {
11 if (isRetired){
12 result = retiredAmount();
13 }
14 else{
15 result = normalPayAmount();
16 }
17 }
18 }
19 return result;
20 }
44
1 const getPayAmount = () => {
2 if (isDead)
3 return deadAmount();
4
5 if (isSeparated)
6 return separatedAmount();
7
8 if (isRetired)
9 return retiredAmount();
10
11 return normalPayAmount();
12 }
else
45
1 //if/else
2 const isRunning = true;
3 if(isRunning){
4 stop()
5 }
6 else{
7 run()
8 }
9 //operador ternario
10 isRunning ? stop() : run()
1 //Negative
2 if(!canNotFormat){
3 format()
4 }
5
6 //Positive
7 if(canFormat){
8 format()
9 }
46
1 const orders = [
2 { productTitle: "Product 1", amount: 10 },
3 { productTitle: "Product 2", amount: 30 },
4 { productTitle: "Product 3", amount: 20 },
5 { productTitle: "Product 4", amount: 60 }
6 ];
7
8 //worse
9 function imperative(){
10 let totalAmount = 0;
11
12 for (let i = 0; i < orders.length; i++) {
13 totalAmount += orders[i].amount;
14 }
15
16 console.log(totalAmount); // 120
17 }
18 //better
19 function declarative(){
20 function sumAmount(currentAmount, order){
21 return currentAmount + order.amount;
22 }
23
24 function getTotalAmount(orders) {
47
25 return orders.reduce(sumAmount, 0);
26 }
27
28 console.log(getTotalAmount(orders)); // 120
29 }
30
31 imperative();
32 declarative();
1 function main(){
2 const stuffList = [
3 { isEnabled: true, name: 'justin' },
4 { isEnabled: false, name: 'lauren' },
5 { isEnabled: false, name: 'max' },
6 ];
7
8 const filteredStuff = stuffList.filter(stuff => !stuff.isEnabled);
9 console.log(filteredStuff);
10 }
11
12 main();
48
⇒
1 //bad
2 function withoutReferentialTransparency(){
3 let counter = 1;
4
5 function increaseCounter(value) {
6 counter = value + 1;
7 }
8
9 increaseCounter(counter);
10 console.log(counter); // 2
11 }
12
13 //better
14 function withReferentialTransparency(){
15 let counter = 1;
16
17 function increaseCounter(value) {
18 return value + 1;
19 }
49
20
21 console.log(increaseCounter(counter)); // 2
22 console.log(counter); // 1
23 }
24
25 withoutReferentialTransparency();
26 withReferentialTransparency();
1 const reportData = {
2 name: "Software Crafters",
3 createdAt: new Date(),
4 purchases: 100,
5 conversionRate: 10,
6 }
7
8 function withOutDRY(){
9 function showReport(reportData) {
10 const reportFormatted = `
50
11 Name: ${reportData.name}
12 Created at: ${reportData.createdAt}
13 Purchases: ${reportData.purchases}
14 Conversion Rate: ${reportData.conversionRate}%`
15 console.log("Showing report", reportFormatted)
16 }
17
18 function saveReport(reportData) {
19 const reportFormatted = `
20 Name: ${reportData.name}
21 Created at: ${reportData.createdAt}
22 Purchases: ${reportData.purchases}
23 Conversion Rate: ${reportData.conversionRate}%`
24 console.log("Saving report...", reportFormatted)
25 }
26
27 showReport(reportData)
28 saveReport(reportData)
29 }
30
31 function withDRY(){
32 function formatReport(reportData){
33 return `
34 Name: ${reportData.name}
35 Created at: ${reportData.createdAt}
36 Purchases: ${reportData.purchases}
37 Conversion Rate: ${reportData.conversionRate}%`
38 }
39
40 function showReport(reportData) {
41 console.log("Showing report...", formatReport(reportData));
42 }
43
44 function saveReport(reportData) {
45 console.log("Saving report...", formatReport(reportData));
46 }
51
47
48 showReport(reportData)
49 saveReport(reportData)
50 }
Command–Query Separation
queries
commands
modifiers mutators
52
1 interface UserRepository
2 {
3 Create(user:User): void;
4 GetByEmail(email:string):User;
5 GetAllByName(string name): User[]
6 }
big O
53
54
55
Prototype ECMAScript
56
1 // Antes de ES6
2 function Person(name) {
3 this.name = name;
4 }
5
6 var person = new Person(“Miguel”);
7 console.log(person.name); // 'Miguel'
1 class Person{
2 constructor(person){
3 this.name = name;
4 }
5 }
6
7 const person = new Person(“miguel”);
8 console.log(person.name);//’Miguel’
57
1 // Antes de ES6
2 function Person(name) {
3 this.name = name;
4 }
5
6 Person.prototype.greet = function(){
7 return “Hola soy “ + this.name;
8 }
9
10 var person = new Person(“Miguel”);
11 console.log(person.greet()); // 'Hola soy Miguel'
1 class Person{
2 constructor(person){
3 this.name = name;
4 }
5
6 greet(){
7 return ‘Hola soy ${this.name}’;
8 }
9 }
10
11 const person = new Person(“miguel”);
12 console.log(person.greet()); // 'Hola soy Miguel'
58
1 // ES5
2 function Programmer(name) {
3 this.name = name;
4 }
5
6 Programmer.prototype = Object.create(Person.prototype);
7
8 Programmer.prototype.writeCode = function(coffee) {
9 if(coffee)
10 console.log( 'Estoy programando ');
11 else
12 console.log('No puedo, no tengo café.');
13 };
14
15 var programmer = new Programmer("Miguel");
16 programmer.greet(); // 'Hola soy Miguel'
17 programmer.writeCode(); //'No puedo, no tengo café'
59
1 // ES6
2 class Programmer extends Person{
3 constructor(name){
4 super(name);
5 }
6
7 writeCode(coffee){
8 coffee ? console.log( 'Estoy programando') : console.log('No puedo,\
9 no tengo café.');
10 }
11 }
12
13 const programmer = new Programmer("Miguel");
14 programmer.greet(); // 'Hola soy Miguel'
15 programmer.writeCode(); //’No puedo, no tengo café’
60
1 class UserSettings {
2 private user: User;
3 private settings: Settings;
4
5 constructor(user) {
6 this.user = user;
7 }
8
9 changeSettings(settings) {
10 if (this.verifyCredentials()) {
11 // ...
12 }
13 }
14
15 verifyCredentials() {
16 // ...
17 }
18 }
61
1 class UserAuth{
2 private user: User;
3
4 constructor(user: User){
5 this.user = user
6 }
7
8 verifyCredentials(){
9 //...
10 }
11 }
12
13 class UserSettings {
14 private user: User;
15 private settings: Settings;
16 private auth: UserAuth;
17
18 constructor(user: User, auth:UserAuth) {
19 this.user = user;
20 this.auth = auth;
21 }
22
23 changeSettings(settings) {
24 if (this.auth.verifyCredentials()) {
25 // ...
26 }
27 }
28 }
62
1 class Post {
2 private title : string;
3 private content: number;
4 private createdAt: number;
5
6 static create(title:string; content:string){
7 return new Post(title, content)
8 }
9
10 private constructor(title:string; content:string){
11 this.setTitle(title);
12 this.setContent(content);
13 this.createdAt = Date.now();
14 }
15
16 setTitle(title:string){
17 if(StringUtils.isNullOrEmpty(title))
63
18 throw new Error(‘Title cannot be empty’)
19
20 this.title = title;
21 }
22
23 setContent(content:string){
24 if(StringUtils.isNullOrEmpty((content))
25 throw new Error(‘Content cannot be empty’)
26
27 this.content = content;
28 }
29
30 getTitle(){
31 return this.title;
32 }
33
34 getContent(){
35 return this.content;
36 }
37 }
64
1 class Employee {
2 private name: string;
3 private email: string;
4
5 constructor(name:string, email:string) {
6 this.name = name;
7 this.email = email;
8 }
9
10 // ...
11 }
12
13 class EmployeeTaxData extends Employee {
14 private ssn: string;
15 private salary: number;
16
17 constructor(ssn:string, salary:number) {
18 super();
19 this.ssn = ssn;
20 this.salary = salary;
21 }
22 //...
23 }
65
1 class EmployeeTaxData{
2 private ssn: string;
3 private salary: number;
4
5 constructor(ssn:string, salary:number) {
6 this.ssn = ssn;
7 this.salary = salary;
8 }
9 //...
10 }
11
12 class Employee {
13 private name: string;
14 private email: string;
15 private taxData: EmployeeTaxData;
16
17 constructor(name:string, email:string) {
18 this.name = name;
19 this.email = email;
20 }
21
22 setTaxData(taxData:EmployeeTaxData){
23 this.taxData = taxData;
24 }
25 // ...
26 }
66
67
68
69
70
code smell
71
1 class Singleton {
2 constructor(){
3 if(Singleton.instance){
4 return Singleton.instance;
5 }
6
7 this.title = "my singleton";
8 Singleton.instance = this;
9 }
10 }
11
12 let mySingleton = new Singleton()
13 let mySingleton2 = new Singleton()
14
15 console.log("Singleton 1: ", mySingleton.title);
16 mySingleton.title = "modified in instance 1"
17 console.log("Singleton 2: ", mySingleton2.title);
72
73
74
75
76
121
1 mkdir testing_1
2 npm init
1 npm install --save-dev jest typescript ts-jest @types/jest
122
1 npx ts-jest config:init
1 "scripts": {
2 "test": "jest",
3 "test:watch": "jest --watchAll"
4 }
123
1 src
2 |--core
3 |--tests
1 export const sum = (a, b) =>
2 a + b;
1 import {sum} from '../core/sum'
2
3 test('should sum two numbers', () => {
4 //Arrange
5 const a = 1;
6 const b = 2;
7 const expected = 3;
8
9 //Act
10 const result = sum(a,b)
11
124
12 //Assert
13 expect(result).toBe(expected);
14 });
125
1 expect(1 + 2).toBe(3)
1 expect(1 + 2).not.toBe(4)
1 test('equality of objects', () => {
2 const data = { one: 1 };
3 data['two'] = 2;
4 const expected = { one: 1, two: 2 }
5
6 expect(data).toEqual(expected);
7 });
126
1 describe('Use Case', () => {
2 test('Should able to do something...', () => {});
3 });
127
1 describe('Use Case', () => {
2 beforeEach(() => {
3 //se ejecuta antes de cada test
4 });
5
6 afterEach(() => {
7 //se ejecuta después de cada test
8 });
9
10 beforeAll(() => {
11 //se ejecuta antes de todos los tests
12 });
13
14 afterAll(() => {
15 //se ejecuta después de todos los tests
16 });
17
18 test('Should able to do something...', () => {
19
20 });
21 });
128
1 "scripts": {
2 "test": "jest",
3 "test:watch": "jest --watchAll",
4 "test:coverage": "jest --coverage"
5 }
129
130
131
132
133
1 describe('Fibonacci should', () => {
2 it('return zero if receive zero', () => {
3 expect(fibonacci(0)).toBe(0);
4 });
5 });
1 function fibonacci(n) {
2 return 0;
3 }
1 it('return one if receive one', () => {
2 expect(fibonacci(1)).toBe(1);
3 });
134
1 function fibonacci(n) {
2 if(n ==0)
3 return 0;
4 else
5 return 1;
6 }
135
1 it('return one if receive two’, () => {
2 expect(fibonacci(2)).toBe(1);
3 });
1 it('returns two if receive three', () => {
2 expect(fibonacci(3)).toBe(2);
3 });
1 function fibonacci(n) {
2 if(n == 0)
3 return 0;
4 if(n == 1 || n == 2)
5 return 1;
6 else
7 return 2;
8 }
136
1 it('returns three if receive four', () => {
2 expect(fibonacci(4)).toBe(3);
3 });
1 function fibonacci(n) {
2 if(n == 0)
3 return 0;
4
5 if(n == 1 || n == 2)
6 return 1;
7
8 else
9 return fibonacci(n - 1) + fibonacci(n - 2);
10 }
1 function fibonacci(n) {
2 const partialFibonacci = (n) =>
3 n == 1
4 ? 1
5 : fibonacci(n - 1) + fibonacci(n - 2)
6
7 return n == 0
8 ? 0
9 : partialFibonacci(n)
10 }
137
138
139
140
141
1 describe('FizzBuzz', () => {
2 it('', () => {
3 expect(true).toBe(true);
4 });
5 });
142
1 function fizzBuzz(n) {
2 return 0;
3 }
4
5 module.exports = fizzBuzz;
1 describe('FizzBuzz', () => {
2 it('should return one if receive one', () => {
3 const expected = 1;
4 const result = fizzBuzz(1)
5
6 expect(result).toBe(expected);
7 });
8 });
143
1 function fizzBuzz(n) {
2 return 1;
3 }
4
5 module.exports = fizzBuzz;
144
1 describe('FizzBuzz', () => {
2 it('should return one if receive one', () => {
3 const expected = 1;
4 const result = fizzBuzz(1);
5
6 expect(result).toBe(expected);
7 });
8
9 it('should return fizz if receive three', () => {
10 const expected = "fizz";
11 const result = fizzBuzz(3)
12
13 expect(result).toBe(expected);
14 });
15 });
145
1 function fizzBuzz(n) {
2 if(n==3)
3 return "fizz";
4
5 return 1;
6 }
1 it('should return buzz if receive five', () => {
2 const expected = "buzz";
3 const result = fizzBuzz(5)
4
5 expect(result).toBe(expected);
6 });
146
1 function fizzBuzz(n) {
2 if(n == 3)
3 return "fizz";
4
5 if(n == 5)
6 return "buzz";
7
8 return 1;
9 }
1 function fizzBuzz(n) {
2 if(n % 3 == 0)
3 return "fizz";
4
5 if(n % 5 == 0)
6 return "buzz";
7
8 return n;
9 }
147
1 it('should return fizzbuzz if receive fifteen', () => {
2 const expected = "fizzbuzz";
3 const result = fizzBuzz(15)
4
5 expect(result).toBe(expected);
6 });
148
1 function fizzBuzz(n) {
2 if(n % 15 == 0)
3 return "fizzbuzz";
4
5 if(n % 3 == 0)
6 return "fizz";
7
8 if(n % 5 == 0)
9 return "buzz";
10
11 return n;
12 }
1 it('should return fizz if receive any number divisible by three', () =>\
2 {
3 const expected = "fizz";
4 const result = fizzBuzz(9)
5
6 expect(result).toBe(expected);
7 });
8
9 it('should return buzz if receive any number divisible by five', () => {
10 const expected = "buzz";
11 const result = fizzBuzz(25)
12
13 expect(result).toBe(expected);
14 });
149
15
16 it('should return buzz if receive any number divisible by fifteen', () \
17 => {
18 const expected = "fizzbuzz";
19 const result = fizzBuzz(30)
20
21 expect(result).toBe(expected);
22 });
23
24 it('should return the same number that receives', () => {
25 const expected = 4;
26 const result = fizzBuzz(4)
27
28 expect(result).toBe(expected);
29 });
150
1 function fizzBuzz(n) {
2 const divisibleBy = (divider, n) => n % divider == 0;
3
4 if(divisibleBy(15, n))
5 return "fizzbuzz";
6
7 if(divisibleBy(3, n))
8 return "fizz";
9
10 if(divisibleBy(5, n))
11 return "buzz";
12
13 return n;
14 }
15
16 module.exports = fizzBuzz;
151
1 import { match } from "x-match-expression";
2
3 export function fizzBuzz(n) {
4 const divisibleBy = divider => n => n % divider === 0;
5
6 return match(n)
7 .case(divisibleBy(15), "fizzbuzz")
8 .case(divisibleBy(5), "buzz")
9 .case(divisibleBy(3), "fizz")
10 .default(n);
11 }
152
153
154
155
156