Добро пожаловать в часть 6 серии. Мы продолжим задавать вопросы по ES6 здесь. Ознакомиться с пятой частью сериала можно здесь.

Вопрос 34- Объясните классы в ES6?
Ответ-Функция конструктора и классы ES6 одинаковы. Классы ES6 — это не что иное, как синтетический сахар для функции конструктора, и внутри они ведут себя точно так же.

Рассмотрим приведенный ниже пример функции конструктора.

То же самое можно написать с классами ES6 с конструктором, но это точно так же.

Вопрос 35- Объясните подклассы и наследование в ES6?
Ответ-Концепция подклассов и наследования такова на других языках, таких как Java и C++.

Рассмотрим приведенный ниже пример. У нас есть родительский класс «Млекопитающее» и подкласс «Летучая мышь», который наследуется от него. Обратите внимание, что мы использовали ключевое слово «extends».
Теперь, чтобы использовать переменные «ноги» и «имя» в «Летучей мыши», мы используем метод «супер()». Также обратите внимание, что мы можем использовать update «walk()» и добавлять дополнительные функции.

class Mammal {
  constructor(_legs, _name="Nabendu") {
    this.legs = _legs;
    this.name = _name;    
  }
  walk() {
    return `${this.name} is walking`;
  }
}
class Bat extends Mammal {
    constructor(_legs, _name, _isVegetarian) {
      super(_legs, _name);
      this.isVegetarian = _isVegetarian;
  }
  fly() {
    return `${this.name} is flying`;
  }
  walk() {
    let eatable = this.isVegetarian ? 'carrot' : 'bug';
    return `${super.walk()} with a ${eatable}`;
  }
}
let fruitBat = new Bat(4,'Bond', true);
console.log(fruitBat.walk()); //Bond is walking with a carrot
let meatBat = new Bat(2, undefined, false);
console.log(meatBat.walk()); // Nabendu is walking with a bug

Вопрос 36- Объясните итераторы в JavaScript?
Ответ-Недавно появился JavaScript [Symbol.iterator] свойство в типах данных. Это указывает, является ли структура данных итерируемой или нет. Это включает в себя массив, строки, карту, наборы и список узлов. Объекты не имеют свойства [Symbol.iterator].

Если мы проверим __proto__ массива, мы сможем найти свойство [Symbol.iterator].

Но это не относится к объектам, поскольку они не являются итерируемыми.

Теперь iterator создаются с помощью «Symbol.iterator» и могут использоваться для перебора всех структур данных, имеющих свойство [Symbol.iterator].

Рассмотрим приведенный ниже пример. Здесь мы создаем итератор по синтаксису в строке 3. Теперь мы можем перебирать массив с помощью «iterator.next()». Каждый запуск дает и Object, у которых есть «значение» и «выполнено», которые указывают, есть ли элемент. Обратите внимание, что после пятого запуска мы получаем «значение» как undefined и «готово» как true.

Вопрос 37- Объясните генераторы в JavaScript?
Ответ-Генератор — это специальный тип функций, который генерирует что-то, когда мы перебираем его с помощью итератора. Они отличаются от массива тем, что не имеют значений заранее, а генерируют их по запросу.

Давайте посмотрим на приведенный ниже пример. Функция генератора имеет специальный синтаксис с «*». Кроме того, внутри генератора есть операторы yield. Каждый «next()» переходит к следующему оператору yield.

В приведенном выше примере не показаны какие-либо преимущества генератора, и это то же самое, что итерация по массиву. Но давайте посмотрим на следующий пример, где у нас есть бесконечный цикл while внутри генератора.

Сначала посмотрите на эту обычную функцию. Если мы запустим его, это вызовет бесконечный цикл и приведет к сбою вашего браузера.

//Normal Function
function normalInfinite() {
  let i = 0;
  while(true) {   
    console.log(i);
    i++;
  }
}
normalInfinite();

Но функция того же типа с генератором не создает бесконечный цикл. Он приостанавливается каждый раз, когда вызывается yield, и будет генерировать следующее значение «i» каждый раз, когда вызывается «next()».

Вопрос 38. Объясните асинхронное ожидание в JavaScript?
Ответ.Асинхронное ожидание — это, по сути, промисы под капотом. Но они упростили реализацию кода обещаний. Если обещания упростили код для обратных вызовов, то асинхронное ожидание упростило код для обещаний.

Давайте сначала проверим пример вложенного промиса. Здесь у нас есть три функции, которые возвращают промисы: cleanRoom, removeGarbage и winIcecream. Теперь, когда запускается функция cleanRoom и из промиса возвращается разрешение, будет запущен немедленный блок .then. В .then мы возвращаем следующее removeGarbage , а в этом .then мы возвращаем winIcecream. Мы передаем сообщение из одной функции в другую, поэтому оно будет добавлено.

let cleanRoom = function() {
  return new Promise(function(resolve, reject) {
     resolve('Cleaned the room,');
  });
};
let removeGarbage = function(message) {
  return new Promise(function(resolve, reject) {
    resolve(message + ' removed Garbage,');
  });
};
let winIcecream = function(message) {
  return new Promise(function(resolve, reject) {
    resolve( message + ' won Icecream');
  });
};
cleanRoom().then(function(result){
                     return removeGarbage(result);
                    }).then(function(result){
                     return winIcecream(result);
                    }).then(function(result){
                     console.log(result + ' All Finished');
                    })
//Cleaned the room, removed Garbage, won Icecream All Finished

Мы рефакторим код с асинхронным ожиданием. Здесь вместо трех функций у нас есть одна функция cleaningTheRoom, перед которой стоит ключевое слово «async». Это означает, что функция будет иметь оператор ожидания. В «ожидании» мы можем получить значение, возвращаемое промисом, сохраненным в переменной. Этот тип кода намного чище, чем обещание.

const cleaningTheRoom = async() => {
  const cleanRoom = new Promise((resolve, reject) => { resolve('Cleaned the room,');});
  const removeGarbage = new Promise((resolve, reject) => { resolve('removed Garbage,');});
  const winIcecream = new Promise((resolve, reject) => { resolve(' won Icecream');});
  
  let roomCleaned = await cleanRoom;
  console.log(roomCleaned);
  
  let garbageRemoved = await removeGarbage;
  console.log(garbageRemoved);
  
  let icecreamWon = await winIcecream;
  console.log(icecreamWon);
  
  
}
cleaningTheRoom().then(() => console.log('All Finished '));
//Cleaned the room, removed Garbage, won Icecream All Finished

То же самое можно еще уменьшить с помощью Promise.all, который переходит на следующую строку только в том случае, если все промисы разрешены. Здесь мы также используем деструктуризацию массива.

const cleaningTheRoom = async() => {
  const cleanRoom = new Promise((resolve, reject) => { resolve('Cleaned the room,');});
  const removeGarbage = new Promise((resolve, reject) => { resolve('removed Garbage,');});
  const winIcecream = new Promise((resolve, reject) => { resolve(' won Icecream');});  
  
  let [roomCleaned, garbageRemoved, icecreamWon] = await Promise.all([cleanRoom, removeGarbage, winIcecream]);
  console.log(`${roomCleaned} ${garbageRemoved} ${icecreamWon}`)
  
  
}
cleaningTheRoom().then(() => console.log(' All Finished '));
//Cleaned the room, removed Garbage, won Icecream All Finished

Теперь мы выполняем обработку ошибок в асинхронном ожидании по-другому, чем в промисах. Обратите внимание, что в промисах у нас может быть блок «.catch» для перехвата ошибок из-за отклонения промиса.

В асинхронном ожидании мы заключаем ожидание в блок try-catch.

const cleaningTheRoom = async() => {
  const cleanRoom = new Promise((resolve, reject) => { reject('Room not cleaned,');});
  const removeGarbage = new Promise((resolve, reject) => { resolve('removed Garbage,');});
  const winIcecream = new Promise((resolve, reject) => { resolve(' won Icecream');});
  
  try {
      let roomCleaned = await cleanRoom;
      console.log(roomCleaned);
  
      let garbageRemoved = await removeGarbage;
      console.log(garbageRemoved);
  
      let icecreamWon = await winIcecream;
      console.log(icecreamWon);
    
  } catch(e) {
    console.log('Error in a promise ');
  }
}
cleaningTheRoom().then(() => console.log('All Finished '));
//Error in a promise All Finished

На этом завершается часть 6 серии и вопросы ES6. Часть 7 вы можете найти здесь.