Добро пожаловать в часть 10 серии и первую часть вопросов по объектно-ориентированному JavaScript. Вы можете найти часть-9 здесь.

В этой части мы будем изучать объектно-ориентированный JavaScript по-старому, что немного сложнее из-за его синтаксиса. Последние классы ES6 — это не что иное, как синтетический сахар, который использует эти концепции только под капотом. Но это одна из важных тем на собеседованиях, где спрашивают о знаниях ООП.

Вопрос 60- Что такое функции-конструкторы в JavaScript?
Ответ-Функции-конструкторы — это специальные функции, используемые для создания его экземпляра. Это JavaScript-эквивалент классов в других языках программирования, таких как C++ и Java.

Давайте начнем с создания простой функции, использующей object для создания объектов сотрудников.

function createEmpObj(fName, lName, gender, desig) {
  var newObj = {};
  newObj.firstName = fName;
  newObj.lastName = lName;
  newObj.gender = gender;
  newObj.designation = desig;
  return newObj;  
}
var emp1 = createEmpObj("Parag", "Khandar", "M", "Project Manager");

Теперь рассмотрим строку var newObj = {}; и return newObj; Они будут одинаковыми в каждой функции, которую мы создадим. Итак, JS предоставил нам особый тип функции, известной как функция-конструктор, для их создания.

Мы рефакторим приведенное выше для использования функции-конструктора. Посмотрите на приведенный ниже код.

function CreateEmpObj(fName, lName, gender, desig) {
  //var this;
  this.firstName = fName;
  this.lastName = lName;
  this.gender = gender;
  this.designation = desig;
  //return this;  
}
var emp2 = new CreateEmpObj("Shikha", "Biswas", "F", "Developer");

Мы добавляем ключевое слово new для создания объекта. В основном он заботится о создании и возврате newObj. Но дает этому новое имя this.
Обратите внимание, что нам не нужно помещать эти две строки в наш код и автоматически помещать их движком JS.

Вопрос 61. Как сделать переменные закрытыми в функциях конструктора в JavaScript?
Ответ. Давайте сначала рассмотрим приведенный ниже код для обычного Функция конструктора. Здесь мы можем получить прямой доступ к переменной «color» с помощью redCar.color. Теперь это может быть нежелательно во многих местах и ​​в традиционных языках, что решается наличием «частных» переменных в классе, а затем наличием «геттеров» и «сеттеров» для доступа к ним.

let Car = function(color) {
  this.color = color;
};
let redCar = new Car('red');
console.log(redCar.color); //red

Теперь мы реализуем то же самое в JavaScript, используя Closures. У нас есть две функции замыкания setColor и getColor, которые в основном являются «сеттерами» и «геттерами». Теперь переменная _color является закрытой и не может быть доступна вне функции с помощью blueCar._color, и мы можем получить к ней доступ только с помощью blueCar.getColor()

let PrivateCar = function(_color) {
 this.setColor = function(color) {
   _color = color;
  };
  
  this.getColor = function() {
   return _color;
  } 
}
let blueCar = new PrivateCar();
blueCar.setColor('blue');
console.log(blueCar._color);  //undefined
console.log(blueCar.getColor()); //blue

Вы можете найти JSFiddle для вышеуказанного здесь.

Вопрос 62- Что такое прототипы и как мы их используем в JavaScript?
Ответ-В JavaScript, как вы знаете, все является Объект. Поэтому всякий раз, когда мы создаем функцию, создается один объект. но на самом деле создается еще один объект, известный как объект-прототип.
Теперь для доступа к объекту Prototype у нас есть ссылка на объект функции, также известный как прототип.

Когда мы создаем экземпляр функции с новым ключевым словом, происходят интересные вещи. Он создает что-то известное как __proto__. Это создается движком JavaScript для каждого вызова функции с использованием нового ключевого слова.

Теперь давайте посмотрим, как создать функцию с использованием прототипа и какие преимущества это дает. В приведенном ниже коде есть две функции haveFun и drinkCoffee. Функция haveFun — это обычная функция внутри функции конструктора. Функция drinkCoffee создается снаружи и добавляется к объекту-прототипу с использованием его эталонного прототипа.

function Employee(name) {
  this.name = name;
  this.haveFun = function() {
    console.log('Play badminton');
  };
}
Employee.prototype.drinkCoffee = function() {
  console.log('coffee break')
}
let emp1 = new Employee('Nabendu');
let emp2 = new Employee('Shikha');
console.log(emp1.haveFun()); //Play badminton
console.log(emp1.drinkCoffee()); //coffee break

Обе функции, кажется, делают одно и то же, тогда какая польза. Преимущество объявления функции с использованием прототипа заключается в том, что она создается один раз в объекте Prototype. Итак, теперь всякий раз, когда мы создаем новый экземпляр функции Constructor, функция больше не создается. Как на снимке экрана ниже, вы можете видеть, что emp1 и emp2 оба имеют name и haveFun. Но drinkCoffee находится внутри __proto__, который является ссылкой на объект Prototype.

Подробнее об объектах и ​​прототипах читайте в моем посте здесь.

Вопрос 63- Объясните Object.create() в JavaScript?
Ответ-Каждый объект создается из глобального Object в JavaScript. Если мы посмотрим на это в консоли, у него много свойств, и создание было одним из них.

Метод Object.create() создает новый объект, используя существующий объект в качестве прототипа вновь созданного объекта.

Рассмотрим приведенный ниже пример, где мы можем создать новый объект из существующего объекта, а также добавить к нему новые свойства или изменить существующее свойство.

const person = {
  isCoder: false,
  printIntroduction: function () {
    console.log(`My name is ${this.name}. Am I coder? ${this.isCoder}`);
  }
};
const me = Object.create(person);
me.name = "Nabendu"; 
me.isCoder = true; 
me.printIntroduction();
// Output: "My name is Nabendu. Am I coder? true"

Теперь давайте посмотрим на практическое использование Object.create в Наследовании. В приведенном ниже примере у нас есть функция Employee, в прототипе которой есть две функции. Затем у нас есть еще одна функция SalesEmployee, и мы устанавливаем ее прототип на прототип сотрудника.
SalesEmployee.prototype = Object.create(Employee.prototype);

const Employee = function() {
}
Employee.prototype =  {
  setName(_name) {
    this.name = _name
  },
  getName() {
    return this.name;    
  }
}
const SalesEmployee = function () {
  
}
SalesEmployee.prototype = Object.create(Employee.prototype);
let emp1 = new SalesEmployee();
console.dir(emp1);
console.log(emp1 instanceof SalesEmployee); //true
console.log(emp1 instanceof Employee); //true
console.log(emp1 instanceof Object); //true
emp1.setName('Shikha');
console.log(emp1.getName()); //Shikha

Затем мы создаем экземпляр SalesEmployee как переменную emp1. Он имеет все функции функции Employee и также может ее использовать.

Вопрос 64- Объясните цепочку прототипов в JavaScript с помощью Object.create() ?
Ответ-Цепочка прототипов может быть объяснена с помощью приведенного ниже примера . Здесь у нас есть три функции Car, ToyCar и ToyTransformer. У каждого есть своя версия print().

Мы наследуем от Car к ToyCar, устанавливая прототип ToyCar в качестве прототипа Car, а затем наследуем от ToyCar к ToyTransformer, устанавливая прототип ToyTransformer в качестве прототипа ToyCar.

Затем у нас есть три экземпляра Car, ToyCar и ToyTransformer. При вызове print() мы получаем соответствующий журнал консоли.

// Car constructor
const Car = function() {};
// Set Car's prototype
Car.prototype = {
  print() {
    return 'This is a Car';
  }
};
// ToyCar constructor
const ToyCar = function() {};
// Set ToyCar's prototype to be Car's prototype
ToyCar.prototype = Object.create(Car.prototype);
// Adding ToyCar's own print method
ToyCar.prototype.print = function(){
 return 'This is a ToyCar';
}

//ToyTransformer constructor
const ToyTransformer = function() {};
// Set ToyTransformer's prototype to be ToyCar's prototype
ToyTransformer.prototype = Object.create(ToyCar.prototype);
// Adding ToyCar's own print method
ToyTransformer.prototype.print = function(){
 return 'This is a ToyTransformer';
};
const toyota = new Car();
const legoCar = new ToyCar();
const bumbleBee = new ToyTransformer();
console.log(toyota.print()); //This is a Car
console.log(legoCar.print()); //This is a ToyCar
console.log(bumbleBee.print()); //This is a ToyTransformer

Теперь давайте удалим метод print() ToyTransformer, а затем, если мы вызовем print() для его экземпляра bumbleBee, мы получим результат от print() ToyCar.

// Car constructor
const Car = function() {};
// Set Car's prototype
Car.prototype = {
  print() {
    return 'This is a Car';
  }
};
// ToyCar constructor
const ToyCar = function() {};
// Set ToyCar's prototype to be Car's prototype
ToyCar.prototype = Object.create(Car.prototype);
// Adding ToyCar's own print method
ToyCar.prototype.print = function(){
 return 'This is a ToyCar';
}
const ToyTransformer = function() {};
// Set ToyCar's prototype to be Car's prototype
ToyTransformer.prototype = Object.create(ToyCar.prototype);
const bumbleBee = new ToyTransformer();
console.log(bumbleBee.print()); //This is a ToyCar

Если мы далее удалим метод print() ToyCar, а затем вызовем print() для его экземпляра bumbleBee, мы получим результат от print() Car.

// Car constructor
const Car = function() {};
// Set Car's prototype
Car.prototype = {
  print() {
    return 'This is a Car';
  }
};
// ToyCar constructor
const ToyCar = function() {};
// Set ToyCar's prototype to be Car's prototype
ToyCar.prototype = Object.create(Car.prototype);
const ToyTransformer = function() {};
// Set ToyCar's prototype to be Car's prototype
ToyTransformer.prototype = Object.create(ToyCar.prototype);
const bumbleBee = new ToyTransformer();
console.log(bumbleBee.print()); //This is a Car

Итак, если вы вызываете метод для дочернего элемента, а этого метода там нет, он проверит свой родитель, а если нет, то проверит родителя родителя.

Вопрос 65- Объясните, как наследование работает в функциях-конструкторах ?
Ответ-Чтобы понять наследование с помощью функции-конструктора, мы рассмотрим пример содержащий функцию Mammal parent и функцию Bat child. Кроме того, сделайте прототип летучей мыши ссылкой на прототип млекопитающего, используя Object.create. Bat’s Prototype будет иметь все методы Mammal’s Prototype.

let Mammal = function(legs) {
  this.legs = legs;
};
Mammal.prototype = {
  walk() {
    return 'walking!';
  }, sleep() {
    return 'sleeping';
  }
};
let Bat = function(legs, isVegetarian) {
  Mammal.call(this, legs);
  this.isVegetarian = isVegetarian;
}

Bat.prototype = Object.create(Mammal.prototype);

Теперь давайте посмотрим на «Bat» в консоли. Как видите, он содержит функцию сна и ходьбы «Млекопитающих» в своем __proto__.

Но у нас есть серьезная проблема, заключающаяся в том, что оператор «Bat.prototype = Object.create(Mammal.prototype);» стирает любую функцию прототипа летучей мыши, а также ее конструктор.
Мы не объявляли никаких функций Bat’s Prototype, но конструктор был стерт.

Итак, мы решаем это с помощью приведенного ниже кода. Он использует Bat.prototype.constructor = Bat; чтобы снова установить конструктор в «Bat».
Кроме того, мы объявляем функцию Bat’s Prototype после «Bat.prototype = Object.create(Mammal.prototype);».

...
...
Bat.prototype = Object.create(Mammal.prototype);
Bat.prototype.constructor = Bat;
Bat.prototype.fly = function() {
  return 'Flying!';
}
console.dir(Bat);

Давайте теперь завершим код и создадим два экземпляра «Bat» как fruitBat и bugBat. Оба могут получить доступ к функциям ходьбы, сна и полета.

let Mammal = function(legs) {
  this.legs = legs;
};
Mammal.prototype = {
  walk() {
    return 'walking!';
  }, sleep() {
    return 'sleeping';
  }
};
let Bat = function(legs, isVegetarian) {
  Mammal.call(this, legs);
  this.isVegetarian = isVegetarian;
}
Bat.prototype = Object.create(Mammal.prototype);
Bat.prototype.constructor = Bat;
Bat.prototype.fly = function() {
  return 'Flying!';
}
let fruitBat = new Bat(4, true);
let bugBat = new Bat(2, false);
console.log(fruitBat.walk()); // walking!
console.log(bugBat.sleep());  // sleeping
console.log(bugBat.fly());    // Flying!

Вопрос 66- Объясните метод Object.setPrototypeOf()?
Ответ-Метод Object.setPrototypeOf() был введен в ES6 для верхнего уровня «Объект ».
Этот метод берет метод одного объекта и делает его доступным для другого объекта.

Это также полезно при реализации наследования с использованием литералов объекта вместо функций-конструкторов (см. вопрос 65).

В приведенном ниже коде у нас есть два объекта — млекопитающее и летучая мышь. Затем, чтобы использовать метод млекопитающих в летучих мышах, мы использовали Object.setPrototypeOf(bat, млекопитающее); .

Кроме того, обратите внимание, что мы обновляем функцию walk() внутри "bat", а также вызываем функцию walk млекопитающего с помощью super.walk().

let mammal =  {
  walk() {
    return 'walking!';
  }
};
let bat = {
  fly() {
    return 'Flying!';    
  },
  walk() {
    return `${super.walk()} and eating`;
  }
}
Object.setPrototypeOf(bat, mammal);
console.log(bat.fly()); //Flying!
console.log(bat.walk()); //walking! and eating

На этом завершается часть 10 серии и первая часть вопросов по объектно-ориентированному JavaScript. Вы можете найти часть-11 здесь.