Классы | JavaScript Camp
В JavaScript используется модель прототипного наследования: каждый объект наследует поля (свойства) и методы объекта-прототипа.
class
Для определения класса используется ключевое🗝️ слово class
:
class MyClass {
// методы класса
constructor() { ... }
method1() { ... }
method2() { ... }
method3() { ... }
...
}
Такой синтаксис📖 называется объявлением🗣️ класса.
Методы в классе не разделяются запятой
Синтаксис📖 классов отличается от литералов объектов. Внутри классов запятые не требуются.
Класс может не иметь названия. С помощью выражения класса можно присвоить класс переменной 🔔 :
const UserClass = class {
// тело класса
}
Классы можно экспортировать в виде модулей. Вот пример экспорта по умолчанию:
export default class User {
// тело класса
}
А вот пример именованного экспорта:
export class User {
// тело класса
}
Класс становится полезным, когда вы создаете экземпляр класса. Экземпляр — это объект, содержащий данные и поведение, описанные 🖊️ классом.
Оператор new
создает🏗️ экземпляр класса в JavaScript таким образом: instance = new Class()
.
Например, вы можете создать🏗️ экземпляр класса User 👤 с помощью оператора new
:
const myUser = new User()
new User()
создает🏗️ экземпляр класса
👤.
Видео
Инициализация: constructor()
constructor(…)
это специальный метод в теле класса, который инициализирует экземпляр. Это место, где вы можете установить начальные значения для полей или выполнить любые настройки объектов.
В следующем примере конструктор устанавливает начальное значение поля name
:
class User {
constructor(name) {
this.name = name
}
}
constructor
класса User
использует один параметр name
, который используется для установки начального значения поля this. name
.
Внутри конструктора значение this
равно вновь созданному🏗️ экземпляру.
Аргументы, используемые для создания экземпляра класса, становятся параметрами конструктора 👇 :
function learnJavaScript() { class User { constructor(name) { name // => ‘Jon Snow’ this.name = name } } const user = new User(‘Jon Snow’) //Здесь можно менять значение return user.name }
Loading…
Параметр name
внутри конструктора имеет значение Jon Snow
.
Если вы не определяете конструктор для класса, создается🏗️ конструктор по умолчанию. Конструктор по умолчанию является пустой функцией⚙️, которая не изменяет экземпляр.
В классе может быть только один метод с именем constructor
.
Отказ от классов
Так как в курсе нашей школы мы учим разрабатывать мобильные приложения с помощью библиотеки React, где нововведение React Hooks позволяет использовать состояние и другие возможности React без написания классов. Поэтому рассказывать о классах больше нет смысла, так как мы от них отказались.
Проблемы?
Пишите в Discord или телеграмм чат, а также подписывайтесь на наши новости
Вопросы:
Какое ключевое🗝️ слово для определения класса?
constructor()
class
this
Методы внутри класса разделяются ли запятой.
true
false
Сколько методов constructor()
может находится в одном классе?
- Неограниченно
- До десяти
- Только один
Для того чтобы понять, на сколько вы усвоили этот урок, пройдите тест в мобильном приложении нашей школы по этой теме или в нашем телеграм боте.
Ссылки:
- MDN web docs
- Learn JavaScript
Contributors ✨
Thanks goes to these wonderful people (emoji key):
Philipp Dvinyaninov 📖 | Dmitriy Vasilev 💵 | Resoner2005 🐛 🎨 🖋 | Navernoss 🖋 🐛 🎨 |
Классы | Карманная книга по TypeScript
- Члены класса (class members)
- Поля
- Конструкторы
- Методы
- Классы и наследование
implements
extends
- Видимость членов (member visibility)
public
protected
private
- Статические члены
- Специальные названия статических членов
- Общие классы
- Параметр типа в статических членах
- Значение
this
в классах во время выполнения кода- Стрелочные функции
- Параметры
this
- Типы
this
- Свойства параметров
- Выражения классов (class expressions)
- Абстрактные классы и члены
- Сигнатуры абстрактных конструкций (abstract construct signatures)
- Отношения между классами
Члены класса (class members)#
Вот пример самого простого класса — пустого:
Такой класс бесполезен, поэтому давайте добавим ему несколько членов.
Поля#
Поле — это открытое (публичное) и доступное для записи свойство класса:
Аннотация типа является опциональной (необязательной), но неявный тип будет иметь значение any
.
Поля могут иметь инициализаторы, которые автоматически запускаются при инстанцировании класса:
Как и в случае с const
, let
и var
, инициализатор свойства класса используется для предположения типа этого свойства:
--strictPropertyInitialization
#Настройка strictPropertyInitialization
определяет, должны ли поля класса инициализироваться в конструкторе.
Обратите внимание
Поля классов должны быть инициализированы в самом конструкторе. TS
не анализирует методы, вызываемые в конструкторе, для обнаружения инициализации, поскольку производный класс может перезаписать такие методы, и члены не будут инициализированы.
Если вы намерены инициализировать поле вне конструктора, можете использовать оператор утверждения определения присвоения (definite assignment assertion operator, !
):
readonly
#Перед названием поля можно указать модификатор readonly
. Это запретит присваивать полю значения за пределами конструктора.
Конструкторы#
Конструкторы класса очень похожи на функции. Мы можем добавлять в них параметры с аннотациями типа, значения по умолчанию и перегрузки:
Однако, между сигнатурами конструктора класса и функции существует несколько отличий:
- Конструкторы не могут иметь параметров типа — это задача возлагается на внешнее определение класса, о чем мы поговорим позже
- Конструкторы не могут иметь аннотацию возвращаемого типа — всегда возвращается тип экземпляра класса
super
#Как и в JS
, при наличии базового класса в теле конструктора, перед использованием this
необходимо вызывать super()
:
В JS
легко забыть о необходимости вызова super
, в TS
— почти невозможно.
Методы#
Метод — это свойство класса, значением которого является функция. Методы могут использовать такие же аннотации типа, что и функции с конструкторами:
Как видите, TS
не добавляет к методам ничего нового.
Обратите внимание
В теле метода к полям и другим методам по-прежнему следует обращаться через this
. Неквалифицированное название (unqualified name) в теле функции всегда будет указывать на лексическое окружение.
Геттеры/сеттеры#
Классы могут иметь акцессоры (вычисляемые свойства, accessors):
TS
имеет несколько специальных правил, касающихся предположения типов в случае с акцессорами:
- Если
set
отсутствует, свойство автоматически становитсяreadonly
- Параметр типа сеттера предполагается на основе типа, возвращаемого геттером
- Если параметр сеттера имеет аннотацию типа, она должна совпадать с типом, возвращаемым геттером
- Геттеры и сеттеры должны иметь одинаковую видимость членов (см. ниже)
Если есть геттер, но нет сеттера, свойство автоматически становится readonly
.
Сигнатуры индекса (index signatures)#
Классы могут определять сигнатуры индекса. Они работают также, как сигнатуры индекса в других объектных типах:
Обычно, индексированные данные лучше хранить в другом месте.
Классы и наследование#
Как и в других объектно-ориентированных языках, классы в JS
могут наследовать членов других классов.
implements
#implements
используется для проверки соответствия класса определенному interface
. При несоответствии класса интерфейсу возникает ошибка:
Классы могут реализовывать несколько интерейсов одновременно, например, class C implements A, B {}
.
Предостережение#
Важно понимать, что implements
всего лишь проверяет, соответствует ли класс определенному интерфейсу. Он не изменяет тип класса или его методов. Ошибочно полагать, что implements
изменяет тип класса — это не так!
В приведенном примере мы, возможно, ожидали, что тип s
будет определен на основе name: string
в check
. Это не так — implements
не меняет того, как проверяется тело класса или предполагаются его типы.
Также следует помнить о том, что определение в интерфейсе опционального свойства не приводит к созданию такого свойства:
extends
#Классы могут расширяться другими классами. Производный класс получает все свойства и методы базового, а также может определять дополнительных членов.
Перезапись методов#
Производный класс может перезаписывать свойства и методы базового класса. Для доступа к методам базового класса можно использовать синтаксис super
. Поскольку классы в JS
— это всего лишь объекты для поиска (lookup objects), такого понятия как «супер-поле» не существует.
TS
обеспечивает, чтобы производный класс всегда был подтипом базового класса.
Пример «легального» способа перезаписи метода:
Важно, чтобы производный класс следовал контракту базового класса. Помните, что очень часто (и всегда легально) ссылаться на экземпляр производного класса через указатель на базовый класс:
Что если производный класс не будет следовать конракту базового класса?
Если мы скомпилируем этот код, несмотря на ошибку, такой «сниппет» провалится:
Порядок инициализации#
Порядок инициализации классов может быть неожиданным. Рассмотрим пример:
Что здесь происходит?
Порядок инициализации согласно спецификации следующий:
- Инициализация полей базового класса
- Запуск конструктора базового класса
- Инициализация полей производного класса
- Запуск конструктора производного класса
Это означает, что конструктор базового класса использует собственное значение name
, поскольку поля производного класса в этот момент еще не инициализированы.
Наследование встроенных типов#
В ES2015
конструкторы, неявно возвращающие объекты, заменяют значение this
для любого вызова super
. Для генерируемого конструктора важно перехватывать потенциальное значение, возвращаемое super
, и заменять его значением this
.
Поэтому подклассы Error
, Array
и др. могут работать не так, как ожидается. Это объясняется тем, что Error
, Array
и др. используют new. target
из ES6
для определения цепочки прототипов; определить значение new.target
в ES5
невозможно. Другие компиляторы, обычно, имеют такие же ограничения.
Для такого подкласса:
вы можете обнаружить, что:
- методы объектов, возвращаемых при создании подклассов, могут иметь значение
undefined
, поэтому вызовsayHello
завершится ошибкой instanceof
сломается между экземплярами подкласса и их экземплярами, поэтому (new MsgError()
)instanceof MsgError
возвращаетfalse
Для решения данной проблемы можно явно устанавливать прототип сразу после вызова super
.
Тем не менее, любой подкласс MsgError
также должен будет вручную устанавливать прототип. В среде выполнения, в которой не поддерживается Object.setPrototypeOf
, можно использовать __proto__
.
Видимость членов (member visibility)#
Мы можем использовать TS
для определения видимости методов и свойств для внешнего кода, т. е. кода, находящегося за пределами класса.
public
#По умолчанию видимость членов класса имеет значение public
. Публичный член доступен везде:
Поскольку public
является дефолтным значением, специально указывать его не обязательно, но это повышает читаемость и улучшает стиль кода.
protected
#Защищенные члены видимы только для подклассов класса, в котором они определены.
Раскрытие защищенных членов#
Производные классы должны следовать контракту базового класса, но могут расширять подтипы базового класса дополнительными возможностями. Это включает в себя перевод protected
членов в статус public
:
Обратите внимание
В производной классе для сохранения «защищенности» члена необходимо повторно указывать модификатор protected
.
Доступ к защищенным членам за пределами иерархии классов#
Разные языки ООП по-разному подходят к доступу к защищенным членам из базового класса:
Java
, например, считает такой подход легальным, а C#
и C++
нет.
TS
считает такой подход нелегальным, поскольку доступ к x
из Derived2
должен быть легальным только в подклассах Derived2
, а Derived1
не является одним из них.
private
#Частные члены похожи на защищенные, но не доступны даже в подклассах, т.е. они доступны только в том классе, где они определены.
Поскольку частные члены невидимы для производных классов, производный класс не может изменять их видимость:
Доступ к защищенным членам между экземплярами#
Разные языки ООП также по-разному подходят к предоставлению доступа экземплярам одного класса к защищенным членам друг друга. Такие языки как Java
, C#
, C++
, Swift
и PHP
разрешают такой доступ, а Ruby
нет.
TS
разрешает такой доступ:
Предостережение#
Подобно другим аспектам системы типов TS
, private
и protected
оказывают влияние на код только во время проверки типов. Это означает, что конструкции вроде in
или простой перебор свойств имеют доступ к частным и защищенным членам:
Для реализации «настоящих» частных членов можно использовать такие механизмы, как замыкания (closures), слабые карты (weak maps) или синтаксис приватных полей класса (private fields, #
).
Статические члены#
В классах могут определеяться статические члены. Такие члены не связаны с конкретными экземплярами класса. Они доступны через объект конструктора класса:
К статическим членам также могут применяться модификаторы public
, protected
и private
:
Статические члены наследуются:
Специальные названия статических членов#
Изменение прототипа Function
считается плохой практикой. Поскольку классы — это функции, вызываемые с помощью new
, некоторые слова нельзя использовать в качестве названий статических членов. К таким словам относятся, в частности, свойства функций name
, length
и call
:
Почему не существует статических классов?#
В некоторых языках, таких как C#
или Java
существует такая конструкция, как статический класс (static class).
Существование этих конструкций обусловлено тем, что в названных языках все данные и функции должны находиться внутри классов; в TS
такого ограничения не существует, поэтому в статических классах нет никакой необходимости.
Например, нам не нужен синтаксис «статического класса», поскольку обычный объект (или функция верхнего уровня) прекрасно справляются с такими задачами:
Общие классы#
Классы, подобно интерфейсам, могут быть общими. Когда общий класс инстанцируется с помощью new
, его параметры типа предполагаются точно также, как и при вызове функции:
В классах, как и в интерфейсах, могут использоваться ограничения дженериков и значения по умолчанию.
Параметр типа в статических членах#
Следующий код, как ни странно, является НЕлегальным:
Запомните, что типы полностью удаляются! Во время выполнения существует только один слот Box.defaultValue
. Это означает, что установка Box<string>.defaultValue
(если бы это было возможным) изменила бы Box<number>. defaultValue
, что не есть хорошо. Поэтому статические члены общих классов не могут ссылаться на параметры типа класса.
Значение
this
в классах во время выполнения кода#TS
не изменяет поведения JS
во время выполнения. Обработка this
в JS
может показаться необычной:
Если кратко, то значение this
внутри функции зависит от того, как эта функция вызывается. В приведенном примере, поскольку функция вызывается через ссылку на obj
, значением this
является obj
, а не экземпляр класса.
TS
предоставляет некоторые средства для изменения такого поведения.
Стрелочные функции#
Если у вас имеется функция, которая часто будет вызываться способом, приводящим к потере контекста, имеет смысл определить такое свойство в виде стрелочной функции:
Это требует некоторых компромиссов:
- Значение
this
будет гарантированно правильным во время выполнения, даже в коде, не прошедшем проверки с помощьюTS
- Будет использоваться больше памяти, поскольку для каждого экземпляра класса будет создаваться новая функция
- В производном классе нельзя будет использовать
super. getName
, поскольку отсутствует входная точка для получения метода базового класса в цепочке прототипов
Параметры
this
#При определении метода или функции начальный параметр под названием this
имеет особое значение в TS
. Данный параметр удаляется во время компиляции:
TS
проверяет, что функция с параметром this
вызывается в правильном контексте. Вместо использования стрелочной функции мы можем добавить параметр this
в определение метода для обеспечения корректности его вызова:
Данный подход также сопряжен с несколькими органичениями:
- Мы все еще имеем возможность вызывать метод неправильно
- Выделяется только одна функция для каждого определения класса, а не для каждого экземпляра класса
- Базовые определения методов могут по-прежнему вызываться через
super
Типы
this
#В классах специальный тип this
динамически ссылается на тип текущего класса:
Здесь TS
предполагает, что типом this
является тип, возвращаемый set
, а не Box
. Создадим подкласс Box
:
Мы также можем использовать this
в аннотации типа параметра:
Это отличается от other: Box
— если у нас имеется производный класс, его метод sameAs
будет принимать только другие экземпляры этого производного класса:
Основанные на
this
защитники типа#Мы можем использовать this is Type
в качестве возвращаемого типа в методах классов и интерфейсах. В сочетании с сужением типов (например, с помощью инструкции if
), тип целевого объекта может быть сведен к более конкретному Type
.
Распространенным случаем использования защитников или предохранителей типа (type guards) на основе this
является «ленивая» валидация определенного поля. В следующем примере мы удаляем undefined
из значения, содержащегося в box
, когда hasValue
проверяется на истинность:
Свойства параметров#
TS
предоставляет специальный синтаксис для преобразования параметров конструктора в свойства класса с аналогичными названиями и значениями. Это называется свойствами параметров (или параметризованными свойствами), такие свойства создаются с помощью добавления модификаторов public
, private
, protected
или readonly
к аргументам конструктора. Создаваемые поля получают те же модификаторы:
Выражения классов (class expressions)#
Выражения классов похожи на определения классов. Единственным отличием между ними является то, что выражения классов не нуждаются в названии, мы можем ссылаться на них с помощью любого идентификатора, к которому они привязаны (bound):
Абстрактные классы и члены#
Классы, методы и поля в TS
могут быть абстрактными.
Абстрактным называется метод или поле, которые не имеют реализации. Такие методы и поля должны находится внутри абстрактного класса, который не может инстанцироваться напрямую.
Абстрактные классы выступают в роли базовых классов для подклассов, которые реализуют абстрактных членов. При отсутствии абстрактных членов класс считается конкретным (concrete).
Рассмотрим пример:
Мы не можем инстанцировать Base
с помощью new
, поскольку он является абстрактным. Вместо этого, мы должны создать производный класс и реализовать всех абстрактных членов:
Обратите внимание
Если мы забудем реализовать абстрактных членов, то получим ошибку.
Сигнатуры абстрактных конструкций (abstract construct signatures)#
Иногда нам требуется конструктор класса, создающий экземпляр класса, производный от некоторого абстрактного класса.
Рассмотрим пример:
TS
сообщает нам о том, что мы пытаемся создать экземпляр абстрактного класса. Тем не менее, имея определение greet
, мы вполне можем создать абстрактный класс:
Вместо этого, мы можем написать функцию, которая принимает нечто с сигнатурой конструктора:
Теперь TS
правильно указывает нам на то, какой конструктор может быть вызван — Derived
может, а Base
нет.
Отношения между классами#
В большинстве случаев классы в TS
сравниваются структурно, подобно другим типам.
Например, следующие два класса являются взаимозаменяемыми, поскольку они идентичны:
Также существуют отношения между подтипами, даже при отсутствии явного наследования:
Однако, существует одно исключение.
Пустые классы не имеют членов. В структурном отношении такие классы являются «супертипами» для любых других типов. Так что, если мы создадим пустой класс (не надо этого делать!), вместо него можно будет использовать что угодно:
JS: Классы по JavaScript. Введение | Кудзанайи Дзвайро
Введение
Мы узнали, что JavaScript — это язык, основанный на прототипах, и каждый объект в JavaScript имеет скрытое внутреннее свойство, называемое [[Prototype]], которое можно использовать для расширения свойств и методов объекта.
До недавнего времени трудолюбивые разработчики использовали функции конструктора для имитации объектно-ориентированного шаблона проектирования в JavaScript. Спецификация языка ECMAScript 2015, часто называемая ES6, ввела классы в язык JavaScript. Классы часто называют «синтаксическим сахаром» по сравнению с прототипами и наследованием, что означает, что они предлагают более чистый и простой синтаксис, не предлагая новых функций.
Классы — это функции
Класс JavaScript — это тип функции. Классы объявляются с помощью ключевого слова class. Мы будем использовать синтаксис выражения функции для инициализации функции и синтаксис выражения класса для инициализации класса.
//Инициализация функции выражением функции
const x = function () {}//Инициализация класса выражением класса
const y = class {}
Ранее мы узнали, что можем получить доступ к [[Prototype ]] объекта с помощью метода Object.getPrototypeOf(). Теперь давайте проверим это на пустой функции, которую мы создали
Object.getPrototypeOf(x)//f () { [собственный код] }
Теперь мы можем проверить то же самое на только что созданном классе.
Object.getPrototypeOf(y)//f () { [собственный код] }
Мы видим, что код, объявленный с помощью функции и класса, возвращает [[Prototype]]. С помощью прототипов мы узнали, что любая функция может стать экземпляром конструктора, используя ключевое слово new.
const x = function() {}//Инициализировать конструктор из функции
const конструкторFromFunction = new x()console.log(constructorFromFunction)//x{}
конструктор: f()
Это относится и к классам
const y = class {}//Инициализация конструктора из класса
conststructorFromClass = new y()console.log(constructorFromClass)//y{}
конструктор: класс
Эти примеры конструкторов прототипов в остальном пусты, но мы можем видеть, как под синтаксисом оба метода достигают одного и того же конечного результата.
Определение класса
В учебнике по прототипам и наследованию мы создали пример, основанный на создании персонажа в текстовой ролевой игре. Давайте продолжим с этим примером здесь, чтобы обновить синтаксис с функций на классы.
Первоначально функция-конструктор инициализировалась с рядом параметров, которые присваивались бы как свойства this, ссылаясь на саму себя. По соглашению первая буква идентификатора должна быть заглавной.
//Инициализация функции-конструктора
function Hero(name, level){
this.name = name
this.name = level
}
}
Новый синтаксис класса имеет аналогичную структуру
class Hero {
конструктор (имя, уровень){
this.name = name
this.level = level
}
}
Мы знаем, что функция-конструктор предназначена для создания чертежа объекта, по капитализации первой буквы инициализатора (что необязательно) и по знакомству с синтаксисом . Ключевое слово class более прямо передает цель нашей функции.
Единственная разница в синтаксисе инициализации заключается в использовании ключевого слова класса вместо функции и присвоении свойств внутри метода конструктора(). Ключевое слово class более прямо передает цель нашей функции
Определение методов
Обычная практика работы с функциями-конструкторами заключается в назначении методов непосредственно прототипу, а не при инициализации, как показано в методе приветствия() ниже.
function Hero(name, level){
this.name = name
this.level = level
}//Добавление метода в конструктор
Hero.prototype.greet = function() {
return `${this. имя} говорит привет.`
}
С классами этот синтаксис упрощается, и метод может быть добавлен непосредственно в класс. Используя сокращенное определение метода, введенное в ES6, определение метода является еще более кратким процессом.
class Hero{
конструктор(имя, уровень) {
this.name = имя
this.level = уровень
} приветствие(){
return `${this.name} говорит привет.`
}
}
Давайте посмотрим на эти свойства и методы в действии. Мы создадим новый экземпляр Hero, используя новое ключевое слово, и назначим некоторые значения
const1 hero1 = new Hero('Varg', 1)
Если мы выведем дополнительную информацию о нашем новом объекте с помощью console.log(hero1), мы можем увидеть более подробную информацию о том, что происходит с инициализацией класса.
Герой {имя: "Варг", уровень: 1}
__proto__:
▶ конструктор: класс Герой
▶ приветствие: ƒ приветствие()
В выводе мы видим, что функции конструктора() и приветствия() были применяется к __proto__ или [[Prototype]] героя1, а не непосредственно как метод объекта hero1. Хотя это очевидно при создании функций-конструкторов, это не очевидно при создании классов. Классы допускают более простой и лаконичный синтаксис, но при этом жертвуют некоторой ясностью процесса.
Расширение класса
Преимущество функций-конструкторов и классов заключается в том, что они могут быть расширены в новые схемы объектов на основе родителя. Это предотвращает повторение кода для объектов, которые похожи, но нуждаются в некоторых дополнительных или более специфических функциях.
Новые функции конструктора могут быть созданы из родителя с помощью метода call. Новые функции конструктора могут быть созданы из родителя с помощью метода call.
//создать новый конструктор из родителя
function Mage(имя, уровень, заклинание){
//Конструктор цепочки с вызовом
Hero. call(this, name, level)this.spell = заклинание
}//Создание нового объекта с использованием прототипа Героя в качестве прототипа для только что созданный объект.
Mage.prototype = Object.create(Hero.prototype)
Теперь мы можем создать новый экземпляр Mage, используя те же свойства, что и Hero, а также новый, который мы добавили
const hero2 = new Mage('Lejon' , 2, 'Волшебная стрела')//Маг {имя: "Леджон", уровень: 2, заклинание: "Волшебная стрела"}
__proto__:
▶ конструктор: ƒ Маг(имя, уровень, заклинание)
В классах ES6 ключевое слово super используется вместо вызова для доступа к родительским функциям. Мы будем использовать расширения для ссылки на родительский класс.
/создание нового класса из родительского класса
Mage extends Herp {
конструктор(имя, уровень, заклинание){
//Конструктор цепи с супер
супер(имя, уровень) //добавляем новое свойство
this.spell = заклинание
}
}
Использование классов в JavaScript: 3 ошибки, которые вы, скорее всего, сделаете, и как их избежать | Адриан АБАБЕЙ
С 2015 года вопрос: «Почему в JavaScript нет классов?» , который раньше был на устах у любого разработчика, уже не актуален. Классы появились в ECMAScript 2015 (ES6). И еще: многие разработчики до сих пор не знают, как они работают. Более того, они не воспринимают классы JS как «настоящие» классы. Вот почему при использовании классов в JavaScript они допускают непреднамеренные ошибки, которые приводят к незаметным ошибкам в их приложениях.
- «Но какие ошибки чаще всего возникают при неправильном обращении с классами JavaScript?»
- «И как я могу избежать их или, по крайней мере, быстро исправить?»
Поскольку, очевидно, вы не можете предотвратить возникновение ошибок, если вы действительно не знаете, какие ошибки вы делаете, верно
Итак, вот 3 наиболее распространенные ошибки, которые вы тоже можете совершить при работе с классами в JavaScript:
Подробнее о классах JavaScript: действительно ли они «настоящие» классы?0138Вот простое, но достаточно ясное определение классов в JavaScript:
По сути, это синтаксический сахар над прототипами (наследование на основе прототипов в JS) с гораздо более простым и понятным синтаксисом.
И само собой разумеется, что такой синтаксис зависит от опыта разработчика: вы можете писать меньше (и чище) кода при создании объектов и решении проблем наследования.
«Но являются ли классы JavaScript «настоящими» классами?» — еще один вопрос, «мучающий» многих JS-программистов.
Итак, позвольте мне пролить свет на эту «дилемму»:
Есть 2 незначительных различия между классами JS (или объектами, подобными классам JS) и так называемыми «настоящими» классы:
- при использовании классов в JavaScript вам необходимо добавить статические атрибуты после того, как вы определили свои классы
- также, нет частных членов
Кроме того, они служат той же практической цели и ведут себя как любая другая классовая система.
Кроме того, я хочу подчеркнуть тот факт, что классы JavaScript НЕ НЕ привносят какую-либо новую объектно-ориентированную модель наследования.
Ошибка № 1. Невыполнение кода JavaScript в «строгом режиме»
Проблемы со строгим режимом неизбежно приведут к ошибкам в вашем приложении.
Но вы, должно быть, прямо сейчас спрашиваете себя: «В чем смысл? Почему код JavaScript (имеется в виду тело класса — этот раздел в фигурных скобках) выполняется в строгом режиме?»
- потому что строгий режим «вызов» классам выдает некоторые из когда-то тихих ошибок
- потому что он запускает код в более строгом синтаксисе и, таким образом, устраняет некоторые из старых несоответствий в JavaScript 9
«Когда возникают ошибки, точнее?»
Если вы забыли выполнить свой код JavaScript — метод конструктора, метод прототипа, статический метод и другие функции — в строгом режиме!
Примечание: еще один способ определить классы JavaScript — использовать выражения класса, которые могут быть как именованными, так и безымянными. Однако имейте в виду, что дублирование имени параметра не будет «разрешено» из-за строгого режима. Это автоматически приведет к… ошибкам.
Ошибка № 2: неправильная обработка подклассов
В какой-то момент при использовании классов в Javascript вам может понадобиться превратить определенный класс в другой дочерний. Или его подкласс, если хотите.
Как правильно это сделать?
Использование ключевого слова «extends» в «выражении класса» или в «объявлении класса». Его роль заключается в создании таксономий классов с одним предком .
Примечание: если у вас есть метод-конструктор в вашем дочернем классе, правильный способ сделать это — сначала вызвать « super()» , а затем ключевое слово « this ».
Предупреждение: помните, что классы JavaScript не расширяют неконструируемые объекты. В этом случае вы можете использовать метод Object.setPrototypeOf() для безопасного наследования от обычного объекта!
Ошибка № 3: Не объявлять классы JS перед их использованием
… что приводит к проблемам с подъемом.
Прежде всего, давайте попробуем определить «подъем»:
Это встроенный в JavaScript механизм, при котором каждая переменная и объявление функции перемещаются в верхнюю часть текущей области видимости (глобальной или локальной) перед выполнением. .
Достаточно ясно?
Кроме того, подъем — это та характеристика JS, которая устанавливает объявления функций (а классы JS воспринимаются как «специальные функции») кроме объявлений класса : последние не поднимаются.
Итак, как предотвратить возникновение проблем с подъемом при использовании классов в JavaScript?
Сначала вы объявляете свой класс — используя ключевое слово «класс», сопровождаемое именем класса — и только после этого вы получаете к нему доступ.