Давайте демистифицируем «новое» ключевое слово JavaScript

1656631570 davajte demistificziruem novoe klyuchevoe slovo javascript

от Синтии Ли

Xs5uT7p1wxeZ1fjARsfzqHlQu3aMbaZtwKt5

В выходные я закончил JavaScript Вилла Сентанса: сложные части. Возможно, это звучит не как прекрасный способ провести выходные, но на самом деле я считаю, что прохождение курса было очень веселым и расслабляющим. Он касался функционального программирования, функций высшего порядка, замыканий и асинхронного JavaScript.

Для меня главным моментом курса было то, как он расширил подходы JavaScript к объектно-ориентированному программированию (ООП) и демистифицировал стоящую за магию новый оператор. Теперь у меня есть всестороннее понимание того, что происходит под капотом, когда новый используется оператор.

Объектно-ориентированное программирование в JavaScript

OjGA-narSWLOzyUTLWqTITXh4qD6KIcE36ag
Фото Ника Карвуниса на Unsplash

Объектно-ориентированное программирование (ООП) – это парадигма программирования, основанная на концепции «объектов». Данные и функции (атрибуты и способы) объединены в объект.

Объект в JavaScript — набор пар ключ-значения. Эти пары ключ-значения являются свойствами объекта. Свойство может являться массивом, функцией, самим объектом или любым простым типом данных, таким как строки или целые числа.

Какие техники у нас есть в наборе инструментов JavaScript для создания объектов?

Предположим, что мы создаем пользователей в разработанной игре. Как мы будем хранить сведения о пользователях, таких как их имена, баллы и реализовать такие методы, как прирост баллов? Вот два варианта сотворения базисного объекта.

Вариант 1 – Литеральная нотация объекта

let user1 = {
  name: "Taylor",
  points: 5,
  increment: function() {
    user1.points++;
  }
};

Литерал объекта JavaScript – это список пар имя-значения, заключенных в фигурные скобки. В вышеприведенном примере создается объект «user1», а связанные с ним данные хранятся в нем.

Вариант 2 — Object.create()

Object.create(proto, [ propertiesObject ])

Object.create методы принимают два аргумента:

  1. proto: объект, который должен быть прототипом вновь созданного объекта. Это должно быть объект или значение null.
  2. propertiesObject: свойства нового объекта. Этот аргумент не обязателен.

В основном, вы переходите в Object.create объект, который вы хотите наследовать, и возвращает новый объект, который наследует объект, который вы ему передали.

let user2 = Object.create(null);

user2.name = "Cam";
user2.points = 8;
user2.increment = function() {
  user2.points++;
}

Вышеприведенные основные параметры создания объектов повторяются. Каждое из них нужно создать вручную.

Как нам это преодолеть?

Решение

Решение 1 — Создание объектов с помощью функции

Простое решение – написать функцию для создания новых пользователей.

function createUser(name, points) {
  let newUser = {};
  newUser.name = name;
  newUser.points = points;
  newUser.increment = function() {
    newUser.points++;
  };
  return newUser;
}

Для создания пользователя необходимо ввести информацию в параметры функции.

let user1 = createUser("Bob", 5);
user1.increment();

Однако функция инкремента в приведенном выше примере является только копией оригинальной функции инкремента. Это неудачный способ написания кода, поскольку любые потенциальные изменения в функции должны быть внесены вручную для каждого объекта.

Решение 2 — Используйте прототипную природу JavaScript

В отличие от объектно-ориентированных языков, таких как Python и Java, JavaScript не имеет классов. Он использует концепцию прототипов и цепочку прототипов для наследования.

Когда вы создаете новый массив, вы автоматически получаете доступ к встроенным методам, таким как Array.join, Array.sortи Array.filter. Это связано с тем, что объекты массива наследуют характеристики от Array.prototype.

CHrqNxf5I7tIo4-CfbSXqC6fnDd2H273ieWJ
Кредит изображения: цепочки прототипов JavaScript, цепочки области действия и производительность Диего Касторина

Каждая функция JavaScript имеет свойство prototype, которое по умолчанию пусто. Вы можете добавлять функции к этому свойству прототипа, и в этой форме оно известно как метод. Когда выполняется унаследованная функция, значение this указывает объект наследования.

function createUser(name, points) {
  let newUser = Object.create(userFunction);
  newUser.name = name;
  newUser.points = points;
  return newUser;
}

let userFunction = {
  increment: function() {this.points++};
  login: function() {console.log("Please login.")};
}

let user1 = createUser("Bob", 5);
user1.increment();

Когда user1 был создан объект, сформирована связь цепи прототипа с userFunction.

Когда user1.increment() находится в стеке вызовов, интерпретатор будет искать user1 в глобальной памяти. Далее он будет искать функцию увеличения, но не найдет ее. Интерпретатор просмотрит следующий объект в цепочке прототипов и найдет там функцию увеличения.

Решение 3 новый и это ключевые слова

OQKKYIojqDyXBnOwHdtZfxKL8YMOCii-2GTl

The новый Оператор используется для создания экземпляра объекта, обладающего функцией конструктора.

Когда мы вызываем функцию-конструктор с помощью new, мы автоматизируем следующие действия:

  • Создается новый объект
  • Это связывает this к объекту
  • Объект-прототип функции-конструктора становится свойством __proto__ нового объекта
  • Он возвращает объект из функции

Это фантастически, потому что благодаря автоматизации код меньше повторяется!

function User(name, points) {
 this.name = name; 
 this.points = points;
}
User.prototype.increment = function(){
 this.points++;
}
User.prototype.login = function() {
 console.log(“Please login.”)
}

let user1 = new User(“Dylan”, 6);
user1.increment();

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

Интерпретатор поднимется вверх по цепочке прототипов и найдет функцию увеличения под свойством прототипа User, которая сама также является объектом с информацией внутри него. Помните – Все функции в JavaScript также являются объектами. Теперь, когда интерпретатор нашел то, что ему нужно, он может создать новый локальный контекст выполнения для запуска user1.increment().

Дополнительное примечание: разница между __proto__ и прототипом

Если вы уже запутались по поводу __proto__ и прототипа, не волнуйтесь! Вы далеко не единственные, кого это смущает.

Прототип – это свойство функции-конструктора, определяющее, что станет свойством __proto__ созданного объекта.

Итак, __proto__ — это созданная ссылка, и эта ссылка известна как связь цепи прототипа.

Решение 4 – ES6 «синтаксический сахар»

svX1DgD7SmEqaQLIchi26EuKUV4toaacQSJG

Другие языки позволяют нам писать наши общие методы в самом «конструкторе» объекта. ECMAScript6 представил класс ключевое слово, позволяющее нам писать классы, напоминающие обычные классы других классических языков. На самом деле, это синтаксический сахар над прототипическим поведением JavaScript.

class User {
  constructor(name, points) {
    this.name = name;
    this.points = points;
  }
  increment () {
    this.points++;
  }
  login () {
    console.log("Please login.")
  }
}

let user1 = new User("John", 12);
user1.increment();

В решении 3 ассоциированные методы были точно реализованы с помощью User.prototype.functionName. В этом решении достигаются те же результаты, но синтаксис выглядит более чистым.

Вывод

Теперь мы узнали больше о разных вариантах создания объектов в JavaScript. Хотя класс декларации и новый оператор относительно прост в использовании, важно понимать, что автоматизировано.

Подытоживая, следующие действия автоматизируются, когда функция конструктора вызывается с помощью новый:

  • Создается новый объект
  • Это связывает this к объекту
  • Объект-прототип функции-конструктора становится свойством __proto__ нового объекта
  • Он возвращает объект из функции

Спасибо, что прочли мою статью, и хлопайте, если она вам понравилась! Ознакомьтесь с другими моими статьями, например «Как я создал свое приложение Pomodoro Clock» и уроки, которые я усвоил на этом пути.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *