JavaScript Crash Course

ООП в JavaScript

8

$ whoami

Інна Іващук

Senior Software Engineer

JS developer, music fan, movie-dependent and Star Wars fan 🤓

May the Force be with you!

5 років із GlobalLogic

приблизно 7 років у веб-розробці

        GitHub page

Зміст:

  1. Принципи ООП

  2. Класи vs функції конструктори

  3. Прототипи

  4. this, execution context та області видимості

  5. .bind(), .call(), .apply()

Принципи ООП

Що таке ООП?

Об'єктно-орієнтоване програмування (in english OOP) – це модель програмування яка базується на стверджені того, що програма це сукупність об’єктів які взаємодіють між собою. Кожен об’єкт в цій моделі є незалежним, і він здатний отримувати, обробляти дані та відправляти ці дані іншим об’єктам.

В ООП використано моделі наслідування, поліморфізму та інкапсуляції.

Наслідування (Inheritance)

Наслідування - принцип об'єктно-орієнтованого програмування, що  дозволяє створювати ієрархічні структури об'єктів. Використовуючи наслідування можна створити загальний клас, який буде визначати характеристики, поведінку і властиві певному набору пов'язаних об'єктів.

Наслідування (Inheritance)

function Car(model) {
  this.model = model;
  
  this.getModel = () => this.model;
  this.drive = () => console.log('Driving....');
}

const BMW = new Car('BMW');
const Volvo = new Car('Volvo');

BMW.drive(); // Driving...
Volvo.drive(); // Driving...

Клас або функція-конструктор

Нащадки або наслідники

BMW   

Volvo

Поліморфізм (Polymorphism)

Поліморфізм (від греч. πολὺ - багато, і μορφή - форма) в мовах програмування - можливість об'єктів з однаковою специфікацією мати різну реалізацію.

Animal

Dog

Cat

.speak() // woof!

.speak() // meow!

class Animal {
    constructor(name) {
        this.name = name;
    }
    sound() { return ''; }
    toString() {
        return Object.getPrototypeOf(this).constructor.name;
    }
}

class Cat extends Animal {
    constructor(name) {
        super(name);
    }

    sound() {
        return 'Meow!';
    }
}

class Dog extends Animal {
    constructor(name) {
        super(name);
    }

    sound() {
        return 'Woof!';
    }
}

const Tom = new Cat('Tom');
Tom.sound(); // Meow!
const Rex = new Dog('Rex');
Rex.sound(); // Woof!

Приклад з використанням класів

Інкапсуляція (Encapsulation)

Інкапсуляція – механізм, який поєднує дані та методи, що обробляють ці дані і захищає і те і інше від зовнішнього впливу або невірного використання.

E

n

c

a

p

s

u

l

a

t

i

o

n

Variables

Methods

Class

const createCounter = () => {
  // A variable defined in a factory or constructor function scope
  // is private to that function.
  let count = 0;
  return ({
  // Any other functions defined in the same scope are privileged:
  // These both have access to the private `count` variable
  // defined anywhere in their scope chain (containing function
  // scopes).
  click: () => count += 1,
  getCount: () => count.toLocaleString()
 });
};

const counter = createCounter();

counter.click();
counter.click();
counter.click();

console.log(
  counter.getCount()
);

Real Encapsulation in JavaScript

Приклад використання

Класи vs функції конструктори

function Cat(name, color) {
  this.name = name;
  this.color = color;

  this.run = function () {
    console.log("I’m running");
  };
  this.sound = function () {
    console.log("Meow! Meow!");
  };
}

const Tom = new Cat("Tom", "grey");
const Simon = new Cat("Simon", "red");

Функція конструктор

class Cat {
    constructor(name, color) {
        this.name = name;
        this.color = color;
    }

    run() {
        console.log(this.name +  " is running");
    }

    sound() {
        console.log("Meow! Meow!");
    }
}

const Felix = new Cat("Felix", "white");

Використання класів

class Cat {
    constructor(name, color) {
        this.name = name;
        this.color = color;
    }

    run() {
        console.log(this.name +  " is running");
    }

    sound() {
        console.log("Meow! Meow!");
    }
}

class LazyCat extends Cat {
  constructor(name, color) {
    super(name, color)
  }
  
  run() {
    console.log(this.name + ' is too lazy to run');
  }
}

const Simon = new LazyCat("Simon", "grey");
const Felix = new Cat("Felix", "white");

Використання класів

Прототипи

Прототипи (Prototype)

Властивість прототип - спочатку порожній об'єкт. До якого можуть бути додані функції і властивості, як і для будь-якого іншого об'єкта.

Кожен об'єкт в JavaScript має "секретну" властивість, яке додається при визначенні або ініціалізації об'єкта. Дана властивість має ім'я __proto__. Саме через нього здійснюється доступ до ланцюжку прототипу.

function

object1

object2

prototype

instance of

links to

__proto__

__proto__

function Cat(name, color) {
  this.name = name;
  this.color = color;
}

Cat.prototype.run = function () {
    console.log("I’m running");
};
Cat.prototype.sound = function () {
    console.log("Meow! Meow!");
};

const Tom = new Cat("Tom", "grey");
const Simon = new Cat("Simon", "red");

Функція конструктор + методи додані в prototype

this, execution context та області видимості

 

Область видимості

Область видимості - важлива концепція, що визначає доступність змінних. Ця концепція є основою замикань, поділяючи змінні на глобальні і локальні. this  - це доступ до області 

Область видимості

 Механізм роботи функцій і змінних в JavaScript відрізняється від інших мов програмування. Існують наступні області видимості:

  • Глобальна -  змінні і функції не перебувають всередині якоїсь функції. Тобто, іншими словами, якщо змінна або функція не перебувають всередині конструкції function, то вони - «глобальні»;

  • Локальна - змінні і функції доступні тільки всередині конструкції function;

  • Блочна - змінні і функції доступні тільки у визначеному блоці (ES6).

 

Ключове слово this

Значення this – це об'єкт, який використовувався для виклику методу. В залежності від області видимості, ключове слово this буде посилатись на відповідний контекст функції, наприклад на глобальний об'єкт (Window в браузері), в іншому випадку - контекст функції.

const user = {
  name: "Michael",
  age: 30,

  sayHi() {
    // this - is "current object"
    console.log(this.name);
  }

};

user.sayHi(); // Michael

Об'єкт на який посилається this можна змінювати, використовуючи call() або bind() aбо apply()

this в стрілочній функції

Стілочна функція немає свого контексту і завжди використовує контекст, де вона задекларована і викликається.

const arrowFn1 = () => {
  console.log(this);
}

function Circle() {
  this.PI = 3.14;
  this.r = 20;
  
  
  this.getArea = () => {
    console.log(this);
    
    return this.PI * Math.pow(this.r, 2);
  }
 }

const cr = new Circle();
cr.getArea();

Область видимості

Область видимості: функції і змінні

const a = 10;

console.log(c); // is not available 

function plusC() {
  const c = 100;
  
  return a + c; // a is available
}

Область видимості: цикли і їх змінні

let res = 0;
console.log(i); // i - is not available

for (let i = 0; i < 10; i++) {
  res += i;
}

console.log(i); // i - also is not available

Область видимості: методи і їх змінні

const array = [1, 2, 3, 4];
let res = 0;

console.log(item); // is not available

array.forEach(item => {
  res += item;
})

console.log(item); // also is not available

Контекст виконання (Execution context)

const x = 10;

function foo() {
   const y = 20;

   function bar() {

        const z = 15;
        const output = x + y + z;
        
        return output;

   }

  return bar;
  
}

foo()();

Global Execution Context

Execution Context (foo)

Execution Context (bar)

.bind(), .call(), .apply()

Зміна контексту виконання з bind()

const module = {
  x: 42,
  getX: function() {
    return this.x;
  }
};

const unboundGetX = module.getX;
console.log(unboundGetX()); // The function gets invoked at the global scope
// expected output: undefined

const boundGetX = unboundGetX.bind(module);
console.log(boundGetX());
// expected output: 42

Зміна контексту виконання з допомогою bind()

const module = {
  x: 42,
  getX: function() {
    return this.x;
  }
};

const unboundGetX = module.getX;
console.log(unboundGetX()); // The function gets invoked at the global scope
// expected output: undefined

const boundGetX = unboundGetX.bind(module);
console.log(boundGetX());
// expected output: 42

Зміна контексту виконання з допомогою call()

function Product(name, price) {
  this.name = name;
  this.price = price;
}

function Food(name, price) {
  Product.call(this, name, price);
  this.category = 'food';
}

console.log(new Food('cheese', 5).name);
// expected output: "cheese"

Зміна контексту виконання з допомогою apply()

function Product(name, price) {
  this.name = name;
  this.price = price;
}

function Food(name, price) {
  Product.apply(this, [name, price]);
  this.category = 'food';
}

console.log(new Food('cheese', 5).name);
// expected output: "cheese"

Можна використовувати як і метод .call() тільки з тією відмінністю, що аргументи потрібно передавати у вигляді масиву

Використання apply() в комбінації з Math

const numbers = [5, 6, 2, 3, 7];

// Find the largest number in array
const max = Math.max.apply(null, numbers);

console.log(max);
// Expected output: 7

// Find the smallest number in array
const min = Math.min.apply(null, numbers);

console.log(min);
// Expected output: 2

// Whithout .apply()
const maxPassingNumbers = Math.max(5,6,3,2,1);
// or
const maxUsingSpread = Math.max(...numbers);

Q & A

_10 JavaScript Crash Course

By Inna Ivashchuk

_10 JavaScript Crash Course

  • 589