Довольно интересен рассказ о языке JavaScript. Для тех, кто не в курсе, ниже приведены некоторые основные моменты популярного многопарадигмального языка:

  • Брендан Эйх (@BrendanEich), программист в Netscape Communications Corporation, создал Mocha всего за 10 дней в 1995 году.
  • Вскоре Mocha будет называться JavaScript - который не имеет ничего общего с Java - и, вероятно, был маркетинговым подходом для увеличения популярности Java.
  • JavaScript был представлен как возможность для обеспечения динамического программирования в то время, когда веб-сайты были в основном статичными.
  • С 1996 года Спецификация языка ECMAScript (ECMA-262) определила и стандартизировала язык JavaScript, теперь в 11-м издании.
  • JavaScript работает примерно в 97% всех браузеров по всему миру.
  • JavaScript стал основным выбором для таких клиентских фреймов, как Angular, React и Vue.js, а также для среды выполнения Node.js.

С тех пор, как JavaScript стал мейнстримом, я участвовал в проектах, в которых так или иначе использовался JavaScript. В те первые дни HTML-файлы ссылались на JavaScript для выполнения простой проверки перед отправкой запросов во внутреннюю службу. Теперь каждый веб-проект, над которым я работал за последние 7 лет, использует клиентскую структуру, полностью построенную на JavaScript.

Тем не менее, JavaScript не свободен от проблем дизайна, которые я отмечал в своей публикации Пройдет ли JavaScript испытание временем? Еще в июне 2017 года.

Один из пунктов, не упомянутых тогда, - это обсуждение того, когда использовать класс, а когда использовать прототип в JavaScript. Моя цель этой статьи - сосредоточиться на этих концепциях - даже при использовании существующей инфраструктуры, такой как Веб-компоненты Salesforce Lightning (LWC).

Концепция прототипа в JavaScript

Для целей этой статьи лучше всего сначала поговорить о концепции прототипа в JavaScript.

В JavaScript все объекты наследуют свойства и методы от прототипа. Рассмотрим следующий пример прототипа:

function Vehicle(vinNumber, manufacturer, productionDate, fuelType) {
  this.manufacturer = manufacturer;
  this.vinNumber = vinNumber;
  this.productionDate = productionDate;
  this.fuelType = fuelType;
}
Vehicle.prototype.vehicleInformation = function() {
  var productionDate = new Date(this.productionDate * 1000);
  var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
  var year = productionDate.getFullYear();
  var month = months[productionDate.getMonth()];
  var day = productionDate.getDate();
var friendlyDate = month + ' '  + day + ', ' + year;
  return this.manufacturer + ' vehicle with VIN Number = ' + this.vinNumber + ' was produced on ' + friendlyDate + ' using a fuel type of ' + this.fuelType;
}

В результате этого кода доступен объект Vehicle, и новый экземпляр может быть создан с помощью следующего кода:

let rogue = new Vehicle('5N1FD4YXN11111111', 'Nissan', 1389675600, 'gasoline');

Имея эту информацию, функция vehicleInformation() может быть вызвана, используя следующий подход:

alert(rogue.vehicleInformation());

Это вызовет диалоговое окно с предупреждением, содержащее это сообщение:

«Автомобиль Nissan с VIN-номером 5N1FD4YXN11111111 был произведен 14 января 2014 г. с использованием бензина»

Как и следовало ожидать, второй прототип под названием SportUtilityVehicle может быть представлен для дальнейшего определения данного типа транспортного средства:

function SportUtilityVehicle(vinNumber, manufacturer, productionDate, fuelType, drivetrain) {
  Vehicle.call(this, vinNumber, manufacturer, productionDate, fuelType);
  this.drivetrain = drivetrain;
}

Теперь мы можем создать SportUtilityVehicle вместо простого Vehicle.

let rogue = new SportUtilityVehicle('5N1FD4YXN11111111', 'Nissan', 1389675600, 'gasoline', 'AWD');

Мы также можем определить новую версию с прототипом SportUtilityVehicle:

SportUtilityVehicle.prototype.vehicleInformation = function() {
  return this.manufacturer + ' vehicle with VIN Number = ' + this.vinNumber + ' utilizes drivetrain = ' + this.drivetrain + ' and runs on ' + this.fuelType;
}

Теперь, когда функция vehicleInformation() вызывается с использованием следующего подхода:

alert(rogue.vehicleInformation());

Появится диалоговое окно с предупреждением, содержащее следующее сообщение:

«Автомобиль Nissan с номером VIN = 5N1FD4YXN11111111 использует трансмиссию = AWS и работает на бензине»

Класс JavaScript

Начиная с ECMAScript 2015 (выпущенного в 6-м издании в июне 2015 г.) в JavaScript появилась концепция класса. Хотя это может вызвать интерес у разработчиков, использующих такие языки, как Java, C # и C ++, цель введения опции класса заключалась в том, чтобы позволить создавать классы с использованием более простого и понятного синтаксиса. Фактически, в документации говорится, что классы - это просто «синтаксический сахар», упрощающий работу разработчика.

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

class Vehicle {
  constructor(vinNumber, manufacturer, productionDate, fuelType) {
    this.manufacturer = manufacturer;
    this.vinNumber = vinNumber;
    this.productionDate = productionDate;
    this.fuelType = fuelType;
  }
vehicleInformation() {
    var productionDate = new Date(this.productionDate * 1000);
    var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
    var year = productionDate.getFullYear();
    var month = months[productionDate.getMonth()];
    var day = productionDate.getDate();
var friendlyDate = month + ' '  + day + ', ' + year;
    return this.manufacturer + ' vehicle with VIN Number = ' + this.vinNumber + ' was produced on ' + friendlyDate + ' using a fuel type of ' + this.fuelType;
  }
}
class SportUtilityVehicle extends Vehicle {
  constructor(vinNumber, manufacturer, productionDate, fuelType, drivetrain) {
    super(vinNumber, manufacturer, productionDate, fuelType);
    this.drivetrain = drivetrain;
  }
vehicleInformation() {
    return this.manufacturer + ' vehicle with VIN Number = ' + this.vinNumber + ' utilizes drivetrain = ' + this.drivetrain + ' and runs on ' + this.fuelType;
  }
}

Если нам нужно добавить геттеры и сеттеры в класс SportUtilityVehicle, класс можно обновить, как показано ниже:

class SportUtilityVehicle extends Vehicle {
  constructor(vinNumber, manufacturer, productionDate, fuelType, drivetrain) {
    super(vinNumber, manufacturer, productionDate, fuelType);
    this.drivetrain = drivetrain;
  }
vehicleInformation() {
    return this.manufacturer + ' vehicle with VIN Number = ' + this.vinNumber + ' utilizes drivetrain = ' + this.drivetrain + ' and runs on ' + this.fuelType;
  }
get drivetrain() {
    return this._drivetrain;
  }
set drivetrain(newDrivetrain) {
    this._drivetrain = newDrivetrain;
  }
}

Как видите, синтаксис напоминает такие языки, как Java или C #. Классовый подход также позволяет функциям и атрибутам, принадлежащим цепочке прототипов, не ссылаться на синтаксис Object.prototype. Единственное требование - конструктор всегда назывался «конструктором».

JavaScript Класс v Прототип

Как отмечалось выше, класс в JavaScript - это просто синтаксический сахар, упрощающий работу разработчиков функций, работающих с JavaScript. Хотя этот подход допускает более общий дизайн для тех, кто исходит из таких языков, как Java, C # или C ++, многие пуристы Javascript вообще не советуют использовать классы.

На самом деле, одна проблема упоминается Михаилом Красновым в статье Пожалуйста, прекратите использовать классы в JavaScript:

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

Майкл приводит еще четыре причины, по которым следует избегать использования классов Javascript, но сторонники варианта класса быстро уменьшили вес его мыслей.

Начиная с 2021 года, я придерживаюсь следующего заявления о миссии любого ИТ-специалиста:

«Сосредоточьте свое время на предоставлении функций / функций, которые увеличивают ценность вашей интеллектуальной собственности. Используйте фреймворки, продукты и услуги для всего остального ».

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

Влияние на веб-компоненты Lightning

Несколько лет назад компания Salesforce представила веб-компоненты Lightning (LWC), о которых я рассказывал в статье Модель программирования Salesforce, предлагающая JavaScript. Почти три года спустя я начинаю говорить о влиянии использования классов и прототипов на разработчиков Salesforce.

Быстрый ответ ... это не имеет значения. Salesforce позволяет веб-компонентам Lightning использовать прототип или класс. Типичная модель наследования в JavaScript - через прототип. Но чтобы обратиться к разработчикам, которые привыкли к классическому наследованию, есть этот синтаксический сахар, который поможет разработчикам реализовать прототипное наследование с помощью подхода, который очень похож на классическое наследование.

Итак, когда дело доходит до LWC - а это все о наследовании, поскольку LWC построил замечательный компонент базового класса, который вы можете расширить, - вы также можете воспользоваться этим синтаксическим сахаром.

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

Вот пример того, как это может выглядеть:

import { LightningElement } from 'lwc';
export default class VehicleComponent extends LightningElement {
  // properties go here
vehicleInformation() {
    return this.manufacturer + ' vehicle with VIN Number = ' + this.vinNumber + ' utilizes drivetrain = ' + this.drivetrain + ' and runs on ' + this.fuelType;
  }
}

Видеть? LWC - зная, что JavaScript дает вам этот синтаксический сахар - упрощает вам задачу.

Заключение

Я признаю, что JavaScript - это не тот язык, на разработку которого я тратил большую часть времени. Помимо разработки клиентов на Angular и небольших усилий с использованием Node.js, моя основная работа в качестве разработчика сервисов часто фокусируется на других языковых вариантах.

В то же время использование подхода классов вместо подхода прототипов обеспечивает аналогичный мост для разработчиков Java, C # и C ++. Хотя здесь нет правильного ответа, важно понимать, как и класс, и прототип работают в JavaScript.

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

Хорошего дня!