Что такое конструктор в программировании
Любой переменной, участвующей в работе программы, требуется память и некоторое начальное значение. Для переменных встроенных типов размещение в памяти обеспечивается компилятором. Для локальных переменных память выделяется из стека программы и занимается для хранения значения данной переменной до тех, пока не закончится время ее жизни. Сложные типы данных также должны размещаться в памяти и уничтожаться когда их время жизни закончилось. Это осуществляется с использованием конструкторов и деструкторов.
имя класса, предназначенная для уничтожения переменных (delete).
Одной из особенностей конструктора и деструктора является то, что в отличие от всех остальных функций, у них нет возвращаемого значения.
4.3.1. Конструкторы
Конструктор по умолчанию
Конструктор, не требующий аргументов, называется конструктором по умолчанию. Конструктор по умолчанию не имеет аргументов и инициализирует все переменные члены какими-либо начальными значениями.
При создании любого экземпляра класса вызывается конструктор. Если при описании экземпляра не указываются никакие параметры – вызывается конструктор по умолчанию:
Полный конструктор
Полный конструктор позволяет явно инициализировать все переменные-члены класса.
Если при описании экземпляра класса в скобках указать параметры, при создании экземпляра класса будет вызван полный конструктор и переменные-члены инициализируются указанными значениями.
Неполный конструктор
Возможен и неполный конструктор, когда в списке параметров указываются не все возможные параметры для инициализации членов класса, а только наиболее часто используемые. Остальные параметры будут инициализированы значениями по умолчанию.
Инициализация переменных-членов класса в конструкторах может осуществляться не только в теле конструктора, но и после оператора :. При этом, во время присваивания переменной-члену значения, будет вызываться не оператор присваивания, а конструктор. Для встроенных типов данных, таких как double или int, это не существенно, но если членами класса являются абстрактные типы, вызов конструктора вместо оператора присваивания будет выполняться быстрее.
Конструктор копирования
Конструктор копирования создает копию уже существующего экземпляра класса, копируя поэлементно переменные-члены. Конструктор копирования также используется при передаче экземпляров класса в функции по значению. Обратите внимание, что экземпляр класса передается в конструктор по константной ссылке.
4.3.2. Деструктор (пример 4.4. Конструктор и деструктор класса Матрица)
Деструктор осуществляет освобождение памяти, например уничтожение объектов размещенных динамически.
В классе Lens никакого динамического размещения не происходило, поэтому деструктор будет пустой, но его наличие все равно обязательно. Для примера реализации деструктора, представим, что имеется класс Matrix, который в конструкторе динамически создает двумерный массив размерности n x m. Тогда деструктор должен освобождать память, которую выделяет конструктор.
Конструктор вызывается в момент создания переменной, деструктор вызывается когда время жизни переменной закончилось, то есть когда встречается закрывающая фигурная скобка > блока, в которой была объявлен экземпляр класса, либо когда вызывается оператор delete при динамическом размещении экземпляра класса.
4.3.3. Проверка правильности параметров. Исключительные ситуации
Конструкторы должны проверять передаваемые им аргументы на корректность значений. Например, показатель преломления не может быть меньше 1. Что делать, если в конструктор были переданы неправильные параметры? Для этого в языке С++ существуют исключительные ситуации.
Класс exception является стандартным базовым классом C++ для всех исключений. Исключения можно сгенерировать в случае возникновения непредвиденной ошибки, например мы предполагаем что при вызове класса Lens никто не будет пытаться задать показатель преломления меньше 1, но при этом такая ситуация возможна, и это может привести к ошибке. Сгенерировать исключительную ситуацию можно при помощи оператора throw:
Для обработки возникшей исключительной ситуации используются try и catch блоки.
В блок try заключается код, в котором предположительно могут возникнуть исключительные ситуации. В нашем случае это вызов конструктора. Кроме того, в этот же блок заключают операторы, которые должны быть пропущены в случае, если исключение возникает. В нашем случае вычисление и вывод на экран параксиальных характеристик не имеет смысл выполнять, если в конструкторе возникла ошибка.
Если при выполнение какого-то оператора из блока try возникает исключение – управление сразу переходит к блоку catch. В блоке catch в скобках указывается тип исключения (exception это наиболее общий вид исключения, возможны и другие типы) и имя исключения. Внутри блока catch необходимо обработать ошибку. В нашем случае мы просто выводим на экран сообщение, в каких-то случаях потребуется более сложная обработка. Функция what() содержит текст, сгенерированный в момент создания исключения.
В результаты выполнения данного блока программы на экран выведется сообщение » Index of refraction should be greater than 1.».
Если никаких исключений в try-блоке не происходит, программа игнорирует его catch-обработчик.
Если исключение было сгенерировано, но перехватывание исключения в блоке try не происходило, функция, содержащая этот оператор, немедленно завершается, и программа пытается найти охватывающий try-блок в вызывающей функции. Если нигде в вызывающих функциях не найдется блок try, программа прервется с сообщением об ошибке.
Конструктор класса
Конструктор схож с методом, но отличается от метода тем, что не имеет явным образом определённого типа возвращаемых данных, не наследуется, и обычно имеет различные правила для рассматриваемых модификаторов. Конструкторы часто выделяются наличием одинакового имени с именем класса, в котором объявляется. Их задача — инициализировать члены объекта и определить инвариант класса, сообщив в случае некорректности инварианта. Корректно написанный конструктор оставит объект в ‘правильном’ статусе. Неизменяемые объекты тоже должны быть проинициализированы конструктором.
Термин конструктор также используется для обозначения одного из тегов, описывающих данные в алгебраическом типе данных. Это использование несколько отличается от описываемого в статье. Для дополнительной информации смотрите Алгебраический тип данных.
В большинстве языков конструктор может быть перегружен, что позволяет использовать несколько конструкторов в одном классе, причём каждый конструктор может иметь различные параметры. Некоторые языки различают несколько особых типов конструкторов:
Содержание
Виды конструкторов
Конструктор по умолчанию
Конструктор не имеющий обязательных аргументов. Используется при создании массивов объектов, вызываясь для создания каждого экземпляра. В отсутствие явно заданного конструктора по умолчанию его код генерируется компилятором (что на исходном тексте, естественно, не отражается).
Конструктор копирования
Конструктор, аргументом которого является ссылка на объект того же класса. Применяется в C++ для передачи объектов в функции по значению.
Конструктор копирования нужен, например, если для хранения данных объекта требуется дополнительно выделяемая память. Если его не будет, то конструктором копирования (сгенерированным компилятором) будут скопированы указатели, адресующие данные прежнего объекта (без выделения новой памяти). Соответственно попытка изменения «копии» повредит оригинал, а вызов деструктора для одного из этих объектов при последующем использовании другого приведёт к обращению в область памяти, уже не принадлежащую программе.
Аргумент должен передаваться именно по ссылке, а не по значению. Это вытекает из коллизии: при передаче объекта по значению (в частности, для вызова конструктора) требуется скопировать объект. Но для того, чтобы скопировать объект, необходимо вызвать конструктор копирования.
Конструктор преобразования
Конструктор, принимающий один агрумент. Задаёт преобразование типа своего аргумента в тип конструктора. Такое преобразование типа неявно применяется только если оно уникально.
Виртуальный конструктор
Конструктор не бывает виртуальным в смысле виртуального метода — для того, чтобы механизм виртуальных методов работал, нужно запустить конструктор, который автоматически настроит таблицу виртуальных методов данного объекта.
является некорректным — директива reintroduce разорвала цепочку переопределения виртуального метода, и в действительности будет вызван конструктор TMotorcycle.Create (а значит, будет создан мотоцикл, а не мопед!)
Синтаксис
Имя конструктора должно совпадать с именем класса. Допускается использовать несколько конструкторов с одинаковым именем, но различными параметрами.
Python
Пример
Delphi
Пример
Пример
Пример
Пример
Eiffel
В Эйфеле подпрограммы, которые инициализируют объекты, называются процедурами создания. Процедуры создания в чём-то подобны конструкторам и в чём-то отличаются. Они имеют следующие характеристики:
Пример
Пример
Необходимо отметить, что в ColdFusion не существует метода-конструктора. Широкое распространение среди сообщества программистов на ColdFusion получил способ вызова метода ‘ init ‘, выступающего в качестве псевдоконструктора.
Пример
Тем не менее, конструктор в PHP версии 4 (и ранее) — метод класса с именем этого же класса.
Упрощенные конструкторы (с псевдокодом)
Конструкторы всегда являются частью реализации классов. Класс (в программировании) описывает спецификации основных характеристик набора объектов, являющихся членами класса, а не отдельные характеристики какого-либо объекта из них. Рассмотрим простую аналогию. Возьмем в качестве примера набор (или класс, используя его более общее значение) учеников некоторой школы. Таким образом мы имеем:
Тем не менее, класс Student — всего лишь общий шаблон (прототип) наших школьников. Для его использования программист создает каждого школьника в виде объекта или сущности (реализации) класса. Этот объект является тем реальным фрагментом данных в памяти, чьи размер, шаблон, характеристики и (в некоторой мере) поведение определяются описанием класса. Обычный способ создания объектов — вызов конструктора (классы в общем случае могут иметь отдельные конструкторы). Например,
Конструктор (программирование)
Конструктор схож с методом, но отличается от метода тем, что не имеет явным образом определённого типа возвращаемых данных, не наследуется, и обычно имеет различные правила для рассматриваемых модификаторов. Конструкторы часто выделяются наличием одинакового имени с именем класса, в котором объявляется. Их задача — инициализировать члены объекта и определить инвариант класса, сообщив в случае некорректности инварианта. Корректно написанный конструктор оставит объект в ‘правильном’ статусе. Неизменяемые объекты тоже должны быть проинициализированы конструктором.
Термин конструктор также используется для обозначения одного из тегов, описывающих данные в алгебраическом типе данных. Это использование несколько отличается от описываемого в статье. Для дополнительной информации смотрите Алгебраический тип данных.
В большинстве языков конструктор может быть перегружен, что позволяет использовать несколько конструкторов в одном классе, причём каждый конструктор может иметь различные параметры. Некоторые языки различают несколько особых типов конструкторов:
Содержание
Виды конструкторов
Конструктор по умолчанию
Конструктор не имеющий обязательных аргументов. Используется при создании массивов объектов, вызываясь для создания каждого экземпляра. В отсутствие явно заданного конструктора по умолчанию его код генерируется компилятором (что на исходном тексте, естественно, не отражается).
Конструктор копирования
Конструктор, аргументом которого является ссылка на объект того же класса. Применяется в C++ для передачи объектов в функции по значению.
Конструктор копирования нужен, например, если для хранения данных объекта требуется дополнительно выделяемая память. Если его не будет, то конструктором копирования (сгенерированным компилятором) будут скопированы указатели, адресующие данные прежнего объекта (без выделения новой памяти). Соответственно попытка изменения «копии» повредит оригинал, а вызов деструктора для одного из этих объектов при последующем использовании другого приведёт к обращению в область памяти, уже не принадлежащую программе.
Аргумент должен передаваться именно по ссылке, а не по значению. Это вытекает из коллизии: при передаче объекта по значению (в частности, для вызова конструктора) требуется скопировать объект. Но для того, чтобы скопировать объект, необходимо вызвать конструктор копирования.
Конструктор преобразования
Конструктор, принимающий один агрумент. Задаёт преобразование типа своего аргумента в тип конструктора. Такое преобразование типа неявно применяется только если оно уникально.
Виртуальный конструктор
Конструктор не бывает виртуальным в смысле виртуального метода — для того, чтобы механизм виртуальных методов работал, нужно запустить конструктор, который автоматически настроит таблицу виртуальных методов данного объекта.
«Виртуальными конструкторами» называют похожий, но другой механизм, присутствующий в некоторых языках — например, он есть в Delphi, но нет в C++ и Java. Этот механизм позволяет создать объект любого заранее неизвестного класса при двух условиях:
является некорректным — директива reintroduce разорвала цепочку переопределения виртуального метода, и в действительности будет вызван конструктор TMotorcycle.Create (а значит, будет создан мотоцикл, а не мопед!)
Синтаксис
Имя конструктора должно совпадать с именем класса. Допускается использовать несколько конструкторов с одинаковым именем, но различными параметрами.
Python
Пример
Delphi
Пример
Некоторые отличия между конструкторами и другими методами Java:
Пример
JavaScript
В JavaScript в качестве конструктора выступает обычная функция. Используется в качестве операнда оператора new. Использует this для обращения к созданному оператором new объекту.
Пример
Пример
Пример
Eiffel
В Эйфеле подпрограммы, которые инициализируют объекты, называются процедурами создания. Процедуры создания в чём-то подобны конструкторам и в чём-то отличаются. Они имеют следующие характеристики:
Пример
ColdFusion
Пример
Необходимо отметить, что в ColdFusion не существует метода-конструктора. Широкое распространение среди сообщества программистов на ColdFusion получил способ вызова метода ‘ init ‘, выступающего в качестве псевдоконструктора.
Пример
Тем не менее, конструктор в PHP версии 4 (и ранее) — метод класса с именем этого же класса.
Упрощенные конструкторы (с псевдокодом)
Конструкторы всегда являются частью реализации классов. Класс (в программировании) описывает спецификации основных характеристик набора объектов, являющихся членами класса, а не отдельные характеристики какого-либо объекта из них. Рассмотрим простую аналогию. Возьмем в качестве примера набор (или класс, используя его более общее значение) учеников некоторой школы. Таким образом мы имеем:
Тем не менее, класс Student — всего лишь общий шаблон (прототип) наших школьников. Для его использования программист создает каждого школьника в виде объекта или сущности (реализации) класса. Этот объект является тем реальным фрагментом данных в памяти, чьи размер, шаблон, характеристики и (в некоторой мере) поведение определяются описанием класса. Обычный способ создания объектов — вызов конструктора (классы в общем случае могут иметь отдельные конструкторы). Например,
Конструктор объекта
Конструктор схож с методом, но отличается от метода тем, что не имеет явным образом определённого типа возвращаемых данных, не наследуется, и обычно имеет различные правила для рассматриваемых модификаторов. Конструкторы часто выделяются наличием одинакового имени с именем класса, в котором объявляется. Их задача — инициализировать члены объекта и определить инвариант класса, сообщив в случае некорректности инварианта. Корректно написанный конструктор оставит объект в ‘правильном’ статусе. Неизменяемые объекты тоже должны быть проинициализированы конструктором.
Термин конструктор также используется для обозначения одного из тегов, описывающих данные в алгебраическом типе данных. Это использование несколько отличается от описываемого в статье. Для дополнительной информации смотрите Алгебраический тип данных.
В большинстве языков конструктор может быть перегружен, что позволяет использовать несколько конструкторов в одном классе, причём каждый конструктор может иметь различные параметры. Некоторые языки различают несколько особых типов конструкторов:
Содержание
Виды конструкторов
Конструктор по умолчанию
Конструктор не имеющий обязательных аргументов. Используется при создании массивов объектов, вызываясь для создания каждого экземпляра. В отсутствие явно заданного конструктора по умолчанию его код генерируется компилятором (что на исходном тексте, естественно, не отражается).
Конструктор копирования
Конструктор, аргументом которого является ссылка на объект того же класса. Применяется в C++ для передачи объектов в функции по значению.
Конструктор копирования нужен, например, если для хранения данных объекта требуется дополнительно выделяемая память. Если его не будет, то конструктором копирования (сгенерированным компилятором) будут скопированы указатели, адресующие данные прежнего объекта (без выделения новой памяти). Соответственно попытка изменения «копии» повредит оригинал, а вызов деструктора для одного из этих объектов при последующем использовании другого приведёт к обращению в область памяти, уже не принадлежащую программе.
Аргумент должен передаваться именно по ссылке, а не по значению. Это вытекает из коллизии: при передаче объекта по значению (в частности, для вызова конструктора) требуется скопировать объект. Но для того, чтобы скопировать объект, необходимо вызвать конструктор копирования.
Конструктор преобразования
Конструктор, принимающий один агрумент. Задаёт преобразование типа своего аргумента в тип конструктора. Такое преобразование типа неявно применяется только если оно уникально.
Виртуальный конструктор
Конструктор не бывает виртуальным в смысле виртуального метода — для того, чтобы механизм виртуальных методов работал, нужно запустить конструктор, который автоматически настроит таблицу виртуальных методов данного объекта.
является некорректным — директива reintroduce разорвала цепочку переопределения виртуального метода, и в действительности будет вызван конструктор TMotorcycle.Create (а значит, будет создан мотоцикл, а не мопед!)
Синтаксис
Имя конструктора должно совпадать с именем класса. Допускается использовать несколько конструкторов с одинаковым именем, но различными параметрами.
Python
Пример
Delphi
Пример
Пример
Пример
Пример
Eiffel
В Эйфеле подпрограммы, которые инициализируют объекты, называются процедурами создания. Процедуры создания в чём-то подобны конструкторам и в чём-то отличаются. Они имеют следующие характеристики:
Пример
Пример
Необходимо отметить, что в ColdFusion не существует метода-конструктора. Широкое распространение среди сообщества программистов на ColdFusion получил способ вызова метода ‘ init ‘, выступающего в качестве псевдоконструктора.
Пример
Тем не менее, конструктор в PHP версии 4 (и ранее) — метод класса с именем этого же класса.
Упрощенные конструкторы (с псевдокодом)
Конструкторы всегда являются частью реализации классов. Класс (в программировании) описывает спецификации основных характеристик набора объектов, являющихся членами класса, а не отдельные характеристики какого-либо объекта из них. Рассмотрим простую аналогию. Возьмем в качестве примера набор (или класс, используя его более общее значение) учеников некоторой школы. Таким образом мы имеем:
Тем не менее, класс Student — всего лишь общий шаблон (прототип) наших школьников. Для его использования программист создает каждого школьника в виде объекта или сущности (реализации) класса. Этот объект является тем реальным фрагментом данных в памяти, чьи размер, шаблон, характеристики и (в некоторой мере) поведение определяются описанием класса. Обычный способ создания объектов — вызов конструктора (классы в общем случае могут иметь отдельные конструкторы). Например,
Примечания
Ссылки
См. также
Полезное
Смотреть что такое «Конструктор объекта» в других словарях:
конструктор класса — Специальный блок инструкций, вызываемый при создании объекта. [ГОСТ Р 54456 2011] Тематики телевидение, радиовещание, видео EN class constructor … Справочник технического переводчика
Конструктор (программирование) — У этого термина существуют и другие значения, см. Конструктор. В объектно ориентированном программировании конструктор класса (от англ. constructor, иногда сокращают ctor) специальный блок инструкций, вызываемый при создании объекта.… … Википедия
Конструктор класса — В объектно ориентированном программировании конструктор класса (от англ. constructor, иногда сокращают ctor) специальный блок инструкций, вызываемый при создании объекта, причём или при его объявлении (располагаясь в стеке или в статической… … Википедия
Конструктор копирования — Конструктором копирования (в англоязычной литературе используется термин copy constructor) называется специальный конструктор в языке программирования C++, применяемый для создания нового объекта как копии уже существующего. Такой конструктор… … Википедия
Класс объекта — Класс, наряду с понятием «объект», является важным понятием объектно ориентированного подхода в программировании (хотя существуют и бесклассовые объектно ориентированные языки, например, Прототипное программирование). Под классом подразумевается… … Википедия
Нуль-арный конструктор — В компьютерном программировании нуль арным конструктором (в англ. языке используется термин nullary constructor) называют конструктор, не принимающий аргументы. Содержание 1 Объектно ориентированные конструкторы 1.1 … Википедия
C++11 — C++11[1][2] или ISO/IEC 14882:2011[3] (в процессе работы над стандартом носил условное наименование C++0x[4][5]) новая версия стандарта языка C++, вместо ранее действовавшего ISO/IEC 14882:2003. Новый стандарт включает дополнения в ядре… … Википедия
C++0x — C++0x будущая версия стандарта языка C++, вместо ныне существующего ISO/IEC 14882:2003. Новый стандарт будет включать дополнения в ядре языка и расширение STL, включая большую часть TR1 кроме, вероятно, библиотеки специальных… … Википедия
TObject — TObject класс, являющийся общим предком всех классов языка Object Pascal. См. также: Free Pascal. TObject инкапсулирует основное поведение всех классов в Object Pascal и отвечает за выделение и освобождение памяти при создании и удалении… … Википедия
C++ — У этого термина существуют и другие значения, см. C. См. также: Си (язык программирования) C++ Семантика: мультипарадигмальный: объектно ориентированное, обобщённое, процедурное, метапрограммирование Тип исполнения: компилируемый Появился в … Википедия
Объектно-ориентированный JavaScript простыми словами
Доброго времени суток, друзья!
В JavaScript существует 4 способа создать объект:
Для того, чтобы ответить на эти вопросы мы не только рассмотрим каждый подход в отдельности, но и сравним между собой классы и фабричные функции по следующим критериям: наследование, инкапсуляция, ключевое слово «this», обработчики событий.
Давайте начнем с того, что такое объектно-ориентированное программирование (ООП).
Что такое ООП?
По сути, ООП — это способ написания кода, позволяющий создавать объекты с помощью одного объекта. В этом также заключается суть шаблона проектирования «Конструктор». Общий объект, обычно, называется планом, проектом или схемой (blueprint), а создаваемые с его помощью объекты — экземплярами (instances).
Каждый экземпляр имеет как свойства, наследуемые от родителя, так и собственные свойства. Например, если у нас имеется проект Human (человек), мы можем создавать на его основе экземпляры с разными именами.
Второй аспект ООП состоит в структурировании кода, когда у нас имеется несколько проектов разного уровня. Это называется наследованием (inheritance) или классификацией (созданием подклассов) (subclassing).
Третий аспект ООП — инкапсуляция, когда мы скрываем детали реализации от посторонних, делая переменные и функции недоступными извне. В этом заключается суть шаблонов проектирования «Модуль» и «Фасад».
Перейдем с способам создания объектов.
Способы создания объекта
Функция-конструктор
Конструкторами являются функции, в которых используется ключевое слово «this».
this позволяет сохранять и получать доступ к уникальным значениям создаваемого экземпляра. Экземпляры создаются с помощью ключевого слова «new».
Класс
Классы являются абстракцией («синтаксическим сахаром») над функциями-конструкторами. Они облегчают задачу создания экземпляров.
Обратите внимание, что constructor содержит тот же код, что и функция-конструктор, приведенная выше. Мы должны это делать, чтобы инициализировать this. Мы может опустить constructor, если нам не требуется присваивать начальные значения.
На первый взгляд, классы кажутся сложнее, чем конструкторы — приходится писать больше кода. Придержите лошадей и не делайте поспешных выводов. Классы — это круто. Чуть позже вы поймете почему.
Экземпляры также создаются с помощью ключевого слова «new».
Связывание объектов
Данный способ создания объектов был предложен Kyle Simpson. В данном подходе мы определяем проект как обычный объект. Затем с помощью метода (который, как правило, называется init, но это не обязательно, в отличие от constructor в классе) мы инициализируем экземпляр.
Для создания экземпляра используется Object.create. После создания экземпляра вызывается init.
Код можно немного улучшить, если вернуть this в init.
Фабричная функция
Фабричная функция — это функция, возвращающая объект. Можно вернуть любой объект. Можно даже вернуть экземпляр класса или связывания объектов.
Вот простой пример фабричной функции.
Для создания экземпляра нам не требуется ключевое слово «new». Мы просто вызываем функцию.
Теперь давайте рассмотрим способы добавления свойств и методов.
Определение свойств и методов
Методы — это функции, объявленные в качестве свойств объекта.
В ООП существует два способа определения свойств и методов:
Определение свойств и методов в конструкторе
Для определения свойства в экземпляре необходимо добавить его в функцию-конструктор. Убедитесь, что добавляете свойство к this.
Методы, обычно, определяются в прототипе, поскольку это позволяет избежать создания функции для каждого экземпляра, т.е. позволяет всем экземплярам использовать одну функцию (такую функцию называют общей или распределенной).
Для добавления свойства в прототип используют prototype.
Создание нескольких методов может быть утомительным.
Можно облегчить себе жизнь с помощью Object.assign.
Определение свойств и методов в классе
Свойства экземпляра можно определить в constructor.
Свойства прототипа определяются после constructor в виде обычной функции.
Создание нескольких методов в классе проще, чем в конструкторе. Для этого нам не нужен Object.assign. Мы просто добавляем другие функции.
Определение свойств и методов при связывании объектов
Для определения свойств экземпляра мы добавляем свойство к this.
Метод прототипа определяется как обычный объект.
Определение свойств и методов в фабричных функциях (ФФ)
Свойства и методы могут быть включены в состав возвращаемого объекта.
При использовании ФФ нельзя определять свойства прототипа. Если вам нужны такие свойства, можно вернуть экземпляр класса, конструктора или связывания объектов (но это не имеет смысла).
Где определять свойства и методы
Где следует определять свойства и методы? В экземпляре или в прототипе?
Многие считают, что для этого лучше использовать прототипы.
Однако на самом деле это не имеет особого значения.
При определении свойств и методов в экземпляре, каждый экземпляр будет расходовать больше памяти. При определении методов в прототипах, память будет расходоваться меньше, но незначительно. Учитывая мощность современных компьютеров, эта разница является несущественной. Поэтому делайте так, как вам удобней, но все же предпочитайте прототипы.
Например, при использовании классов или связывания объектов, лучше использовать прототипы, поскольку в этом случае код легче писать. В случае ФФ прототипы использовать нельзя. Можно определять только свойства экземпляров.
Прим. пер.: позволю себе не согласиться с автором. Вопрос использования прототипов вместо экземпляров при определении свойств и методов — это не только вопрос расходования памяти, но, прежде всего, вопрос назначения определяемого свойства или метода. Если свойство или метод должны быть уникальными для каждого экземпляра, тогда они должны определяться в экземпляре. Если свойство или метод должны быть одинаковыми (общими) для всех экземпляров, тогда они должны определяться в прототипе. В последнем случае при необходимости внесения изменений в свойство или метод достаточно будет внести их в прототип, в отличие от свойств и методов экземпляров, которые корректируются индивидуально.
Предварительный вывод
На основе изученного материала можно сделать несколько выводов. Это мое личное мнение.
Классы против ФФ — Наследование
Прежде чем переходить к сравнению классов и ФФ, необходимо познакомиться с тремя концепциями, лежащими в основе ООП:
Что такое наследование?
В JavaScript наследование означает передачу свойств от родительского объекта к дочернему, т.е. от проекта к экземпляру.
Это происходит двумя способами:
Понимание создания подклассов
Создание подклассов — это когда дочерний проект расширяет родительский.
Рассмотрим это на примере классов.
Создание подклассов с помощью класса
Для расширения родительского класса используется ключевое слово «extends».
Например, давайте создадим класс «Developer», расширяющий класс «Human».
Класс «Developer» будет расширять Human следующим образом:
Ключевое слово «super» вызывает constructor класса «Human». Если вам это не нужно, super можно опустить.
Допустим, Developer умеет писать код (кто бы мог подумать). Добавим ему соответствующий метод.
Вот пример экземпляра класса «Developer».
Создание подклассов с помощью ФФ
Для создания подклассов с помощью ФФ необходимо выполнить 4 действия:
Создадим подкласс «Developer». Вот как выглядит ФФ «Human».
Добавляем ему метод «code».
Создаем экземпляр Developer.
Перезапись родительского метода
Иногда возникает необходимость перезаписать родительский метод внутри подкласса. Это можно сделать следующим образом:
Тот же процесс с использованием ФФ.
Наследование против композиции
Разговор о наследовании редко обходится без упоминания композиции. Эксперты вроде Eric Elliot считают, что всегда, когда это возможно, следует использовать композицию.
Что же такое композиция?
Понимание композиции
По сути, композиция — это объединение нескольких вещей в одну. Наиболее распространенным и самым простым способом объединения объектов является использование Object.assign.
Композицию легче всего объяснить на примере. Допустим, у нас имеется два подкласса, Developer и Designer. Дизайнеры умеют разрабатывать дизайн, а разработчики — писать код. Оба наследуют от класса «Human».
Теперь предположим, что мы хотим создать третий подкласс. Этот подкласс должен быть смесью дизайнера и разработчика — он должен уметь как разрабатывать дизайн, так и писать код. Назовем его DesignerDeveloper (или, если угодно, DeveloperDesigner).
Как нам его создать?
Мы не может одновременно расширить классы «Designer» и «Developer». Это невозможно, поскольку мы не можем решить, какие свойства должны быть первыми. Это называется проблемой ромба (ромбовидным наследованием).
Проблема ромба может быть решена с помощью Object.assign, если мы отдадим одному объекту приоритет над другим. Однако, в JavaScript не поддерживается множественное наследование.
Здесь нам пригодится композиция.
Данный подход утверждает следующее: вместо создания подкласса «DesignerDeveloper», создайте объект, содержащий навыки, которые можно включать в тот или иной подкласс по необходимости.
Реализация этого подхода приводит к следующему.
Нам больше не нужен класс «Human», ведь мы можем создать три разных класса с помощью указанного объекта.
Вот код для DesignerDeveloper.
Мы можем сделать тоже самое для Designer и Developer.
Вы заметили, что мы создаем методы в экземпляре? Это лишь один из возможных вариантов. Мы также можем поместить методы в прототип, но я нахожу это лишним (при таком подходе кажется, что мы вернулись к конструкторам).
Используйте тот подход, который считаете самым подходящим. Результат будет одинаковым.
Композиция с помощью ФФ
Композиция с помощью ФФ заключается в добавлении распределенных методов в возвращаемый объект.
Наследование и композиция
Никто не говорил, что мы не можем использовать наследование и композицию одновременно.
Возвращаясь к примеру с Designer, Developer и DesignerDeveloper, нельзя не отметить, что они также являются людьми. Поэтому они могут расширять класс «Human».
Вот пример наследование и композиции с использованием синтаксиса классов.
А вот тоже самое с использованием ФФ.
Подклассы в реальном мире
Несмотря на то, что многие эксперты утверждают, что композиция по сравнению с подклассами является более гибкой (и поэтому более полезной), подклассы нельзя сбрасывать со счетов. Многие вещи, с которыми мы имеем дело, основаны на этой стратегии.
Например: событие «click» является MouseEvent (событием мыши). MouseEvent — это подкласс UIEvent (событие пользовательского интерфейса), который, в свою очередь, является подклассом Event (событие).
Другой пример: HTML Elements (элементы) являются подклассами Nodes (узлов). Поэтому они могут использовать все свойства и методы узлов.
Предварительный вывод относительно наследования
Наследование и композиция могут использоваться как в классах, так и в ФФ. В ФФ композиция выглядит «чище», но это незначительное преимущество перед классами.
Классы против ФФ — Инкапсуляция
По сути, инкапсуляция — это скрытие одной вещи внутри другой, из-за чего внутренняя сущность становится недоступной снаружи.
В JavaScript скрываемыми сущностями являются переменные и функции, которые доступны только в текущем контексте. В данном случае контекст — это тоже самое, что область видимости.
Простая инкапсуляция
Простейшей формой инкапсуляции является блок кода.
Находясь в блоке, можно получить доступ к переменной, объявленной за его пределами.
Обратите внимание, что переменные, объявленные с помощью ключевого слова «var», имеют глобальную или фукнциональную область видимости. Старайтесь не использовать var для объявления переменных.
Инкапсуляция с помощью функции
Функциональная область видимости похожа на блочную. Переменные, объявленные в функции, доступны только внутри нее. Это относится ко всем переменным, даже объявленным с помощью var.
Когда же мы находимся внутри функции, то имеем доступ к переменным, объявленным за ее пределами.
Функции могут возвращать значения, которые могут быть использованы впоследствии за пределами функции.
Замыкание
Замыкание — это продвинутая форма инкапсуляции. Это просто функция внутри другой функции.
Переменные, объявленные в outsideFunction, могут использоваться в insideFunction.
Инкапсуляция и ООП
При создании объектов мы хотим, чтобы одни свойства были открытыми (публичными), а другие закрытыми (частными или приватными).
Рассмотрим пример. Скажем, у нас имеется проект «Car». При создании нового экземпляра мы добавляем ему свойство «fuel» (топливо) со значением 50.
Пользователи могут использовать это свойство для определения количества оставшегося топлива.
Пользователи также могут самостоятельно устанавливать количество топлива.
Давайте добавим условие, согласно которому бак автомобиля вмещает максимум 100 литров топлива. Мы не хотим, чтобы пользователи имели возможность самостоятельно устанавливать количество топлива, потому что они могут сломать машину.
Существует два способа это сделать:
Частные свойства по соглашению
В JavaScript частные переменные и свойства, обычно, обозначаются с помощью нижнего подчеркивания.
Как правило, мы создаем методы для управления частными свойствами.
Для определения и установки количества топлива пользователи должны использовать методы «getFuel» и «setFuel», соответственно.
Но переменная «_fuel» в действительности не является частной. Она доступна извне.
Для ограничения доступа к переменным следует использовать настоящие частные поля.
По-настоящему частные поля
Поля — это термин, объединяющий переменные, свойства и методы.
Частные поля классов
Классы позволяют создавать частные переменные с помощью префикса «#».
К сожалению, данный префикс нельзя использовать в конструкторе.
Частные переменные должны определяться вне конструктора.
В данной случае мы можем инициализировать переменную при определении.
Теперь переменная «#fuel» доступна только внутри класса. Попытка получить к ней доступ за пределами класса приведет к возникновению ошибки.
Для управления переменной нам нужны соответствующие методы.
Лично я предпочитаю использовать для этого геттеры и сеттеры. Я нахожу такой синтаксис более читаемым.
Частные поля ФФ
ФФ создают частные поля автоматически. Нам нужно лишь объявить переменную. Пользователи не смогут получить доступ к этой переменной извне. Это происходит благодаря тому, что переменные имеют блочную (или функциональную) область видимости, т.е. являются инкапсулированными по умолчанию.
Для управления частной переменной «fuel» также используются геттеры и сеттеры.
Вот так. Легко и просто!
Предварительный вывод относительно инкапсуляции
Инкапсуляция с помощью ФФ проще и легче для восприятия. Она основана на области видимости, которая является важной частью JavaScript.
Инкапсуляция с помощью классов предполагает использование префикса «#», что может быть несколько утомительным.
Классы против ФФ — this
this — главный аргумент против использования классов. Почему? Потому что значение this зависит от того, где и как this используется. Поведение this часто сбивает с толку не только новичков, но и опытных разработчиков.
Однако, на самом деле концепция this не так уж и сложна. Всего существует 6 контекстов, в которых может использоваться this. Если вы разбираетесь в этих контекстах, у вас не должно возникать проблем с this.
Названными контекстами являются:
Использование this в классах
При использовании в классе this указывает на создаваемый экземпляр (контекст свойства/метода). Вот почему экземпляр инициализируется в constructor.
Использование this в функциях-конструкторах
При использовании this внутри функции и new для создания экземпляра, this будет указывать на экземпляр.
В отличии от ФК в ФФ this указывает на window (в контексте модуля this вообще имеет значение «undefined»).
Таким образом, в ФФ не следует использовать this. В этом состоит одно из основных отличий между ФФ и ФК.
Использование this в ФФ
Для того, чтобы иметь возможность использовать this в ФФ, необходимо создать контекст свойства/метода.
Несмотря на то, что мы можем использовать this в ФФ, нам это не нужно. Мы можем создать переменную, указывающую на экземпляр. Такая переменная может использоваться вместо this.
human.firstName является более точным, нежели this.firstName, поскольку human явно указывает на экземпляр.
На самом деле нам даже не нужно писать human.firstName. Мы можем ограничиться firstName, поскольку данная переменная имеет лексическую область видимости (это когда значение переменной берется из внешнего окружения).
Рассмотрим более сложный пример.
Сложный пример
Условия таковы: у нас имеется проект «Human» со свойствами «firstName» и «lastName» и методом «sayHello».
Также у нас имеется проект «Developer», наследующий от Human. Разработчики умеют писать код, поэтому у них должен быть метод «code». Кроме того, они должны заявлять о своей принадлежности к касте разработчиков, поэтому нам необходимо перезаписать метод «sayHello».
Реализуем указанную логику с помощью классов и ФФ.
Классы
Создаем проект «Human».
Создаем проект «Developer» с методом «code».
Перезаписываем метод «sayHello».
ФФ (с использованием this)
Создаем проект «Human».
Создаем проект «Developer» с методом «code».
Перезаписываем метод «sayHello».
ФФ (без this)
Поскольку firstName за счет лексической области видимости доступна напрямую мы можем опустить this.
Предварительный вывод относительно this
Простыми словами, классы требуют использования this, а ФФ нет. В данном случае я предпочитаю использовать ФФ, поскольку:
Классы против ФФ — Обработчики событий
Во многих статьях по ООП упускается из виду тот факт, что как фронденд-разработчики мы постоянно имеем дело с обработчиками событий. Именно они обеспечивают взаимодействие с пользователями.
Поскольку обработчики событий изменяют контекст this, работа с ними в классах может быть проблематичной. В тоже время в ФФ таких проблем не возникает.
Однако изменение контекста this не имеет значения, если мы знаем, как с этим справиться. Рассмотрим простой пример.
Создание счетчика
Для создания счетчика воспользуемся полученными знаниями, включая частные переменные.
Наш счетчик будет содержать две вещи:
Вот как может выглядеть разметка:
Создание счетчика с помощью класса
Для облегчения задачи попросим пользователя найти и передать разметку счетчика классу «Counter»:
В классе необходимо получить 2 элемента:
Далее мы инициализируем переменную «count» текстовым содержимым countElement. Указанная переменная должна быть частной.
При нажатии кнопки значение счетчика должно увеличиваться на 1. Реализуем это с помощью метода «increaseCount».
Теперь нам необходимо обновить DOM. Реализуем это с помощью метода «updateCount», вызываемого внутри increaseCount:
Осталось добавить обработчик событий.
Добавление обработчика событий
Добавим обработчик к this.buttonElement. К сожалению, мы не можем использовать increaseCount в качестве функции обратного вызова. Это приведет к ошибке.
Исключение выбрасывается, потому что this указывает на buttonElement (контекст обработчика событий). В этом можно убедиться, если вывести значение this в консоль.
Значение this необходимо изменить таким образом, чтобы оно указывало на экземпляр. Это можно сделать двумя способами:
Добавление обработчика событий с помощью bind
bind возвращает новую функцию. В качестве первого аргумента ему передается объект, на который будет указывать this (к которому this будет привязан).
Это работает, но выглядит это не очень хорошо. К тому же bind — это продвинутая функция, с которой сложно иметь дело новичкам.
Стрелочные функции
Стрелочные функции, помимо прочего, не имеют собственного this. Они заимствуют его из лексического (внешнего) окружения. Поэтому код счетчика может быть переписан следующим образом:
Есть еще более простой способ. Мы можем создать increaseCount в виде стрелочной функции. В этом случае this будет указывать на экземпляр.
Вот полный код примера:
Создание счетчика с помощью ФФ
Начало аналогичное — мы просим пользователя найти и передать разметку счетчика:
Получаем необходимые элементы, которые по умолчанию будут частными:
Инициализируем переменную «count»:
Значение счетчика будет увеличиваться с помощью метода «increaseCount». Вы можете использовать обычную функцию, но я предпочитаю другой подход:
DOM будет обновляться с помощью метода «updateCount», который вызывается внутри increaseCount:
Обратите внимание, что вместо this.updateCount мы используем counter.updateCount.
Добавление обрабочика событий
Мы можем добавить обработчик событий к buttonElement, используя counter.increaseCount в качестве колбэка.
Это будет работать, поскольку мы не используем this, поэтому для нас не имеет значения то обстоятельство, что обработчик меняет контекст this.
Первая особенность this
Вы можете использовать this в ФФ, но только в контексте метода.
В следующем примере при вызове counter.increaseCount будет вызван counter.updateCount, поскольку this указывает на counter:
Однако обработчик событий работать не будет, потому что значение this изменилось. Данная проблема может быть решена с помощью bind, но не с помощью стрелочных функций.
Вторая особенность this
При использовании синтаксиса ФФ, мы не можем создавать методы в виде стрелочных функций, потому что методы создаются в контексте функции, т.е. this будет указывать на window:
Поэтому при использовании ФФ я настоятельно рекомендую избегать использования this.
Вердикт относительно обработчиков событий
Обработчики событий меняют значение this, поэтому использовать this следует крайне осторожно. При использовании классов советую создавать колбэки обработчиков событий в виде стрелочных функций. Тогда вам не придется прибегать к услугам bind.
При использовании ФФ рекомендую вообще обходиться без this.
Заключение
Итак, в данной статье мы рассмотрели четыре способа создания объектов в JavaScript:
Во-вторых, мы увидели, что подклассы легче создавать с помощью классов. Однако, в случае композиции лучше использовать ФФ.
В-третьих, мы резюмировали, что, когда речь идет об инкапсуляции, ФФ имеют преимущество перед классами, поскольку последние требуют использования специального префикса «#», а ФФ делают переменные частными автоматически.
В-четвертых, ФФ позволяют обойтись без использования this в качестве ссылки на экземпляр. В классах приходится прибегать к некоторым хитростям, дабы вернуть this исходный контекст, измененный обработчиком событий.
На этом у меня все. Надеюсь, статья вам понравилась. Благодарю за внимание.



