Метод в функции конструкторе js
Функции JavaScript определяются при помощи ключевого слова function.
Вы можете использовать либо декларацию функции, либо функцию-выражение.
Декларация функции
Ранее в этом учебнике мы узнали, что декларация функции имеет следующий синтаксис:
Декларированные функции выполняются не сразу. Они имеют статус "отложенного использования" и выполняются только тогда, когда к ним обратятся (вызовут).
Внимание! Точка с запятой используется для разделения выполняемых выражений JavaScript. Так как декларация функции не является выполняемым выражением, в конце нее обычно не ставят точку с запятой.
Функции-выражения
Также функции JavaScript могут определяться с использованием выражения.
Функции-выражения могут храниться в переменных:
После того как функция-выражение сохранена в переменной, эта переменная может использоваться как функция:
На самом деле функция в предыдущем примере является анонимной (функция без имени).
Функциям, сохраненным в переменных, не нужны имена. Они вызываются при помощи имени переменной.
Внимание! В конце функции из предыдущего примера стоит точка с запятой, так как она часть выполняемого выражения.
Конструктор Function()
Как вы видели в предыдущих примерах, функции JavaScript определяются при помощи ключевого слова function.
Однако, кроме этого функции также могут определяться при помощи встроенной функции конструктора Function().
Тем не менее, на самом деле вам не нужно использовать функцию конструктора.
Предыдущий пример аналогичен следующему коду:
В большинстве случаев в JavaScript можно избежать использование ключевого слова new.
Поднятие функций
Ранее в этом учебнике вы уже познакомились с таким понятием, как "поднятие" переменных. (Глава Поднятие переменных в Javascript)
"Поднятие" — это такое поведение JavaScript, при котором декларации перемещаются (поднимаются) в начало текущей области видимости.
Поднятие применяется к декларациям переменных и функций.
Благодаря этому функции JavaScript можно вызывать до того, как они будут декларированы:
Внимание! Функции, декларированные как выражения, не поднимаются.
Самовызываемые функции
Функции-выражения могут быть "самовызываемыми".
Самовызываемые выражения вызываются (начинают работать) автоматически без специального вызова.
Функция-выражение будет выполняться автоматически, если после выражения стоят круглые скобки ().
При этом декларация функции не может быть самовызываемой. Чтобы указать, что это функция-выражение, необходимо заключить ее в круглые скобки:
Функция в предыдущем примере это анонимная самовызываемая функция.
Функции могут использоваться как значения
Функции JavaScript могут использоваться как значения:
Функции JavaScript могут использоваться в выражениях:
Функции — это объекты
Оператор typeof в JavaScript возвращает для функций тип "function". Однако лучше всего функции JavaScript можно описать как объекты.
У функций JavaScript есть свойства и методы.
Свойство arguments.length возвращает количество параметров, полученных при вызове функции:
Метод toString() возвращает функцию как строку:
Функция, определенная как свойство объекта, называется методом объекта.
Функция, предназначенная для создания новых объектов, называется конструктором объекта.
"Стрелочные" функции
"Стрелочные" функции предоставляют короткую запись для выражений-функций.
При этом в выражении-функции не нужно использовать ключевые слова function, return и фигурные скобки:
У стрелочных функций нет своего значения this. И они не подходят для определения методов объектов.
Стрелочные функции не поднимаются. Они должны определяться до их использования.
Для декларации стрелочных функций использовать ключевое слова const безопаснее ключевого слова var, потому что по своей сути функция — это константное значение.
Не писать ключевое слово return и фигурные скобки можно только, если функция состоит из одного выражения.
Объекты обычно создаются, чтобы представлять сущности реального мира, будь то пользователи, заказы и так далее:
И так же, как и в реальном мире, пользователь может совершать действия: выбирать что-то из корзины покупок, авторизовываться, выходить из системы, оплачивать и т.п.
Такие действия в JavaScript представлены свойствами-функциями объекта.
Примеры методов
Для начала давайте научим нашего пользователя user здороваться:
Здесь мы просто использовали Function Expression (функциональное выражение), чтобы создать функцию для приветствия, и присвоили её свойству user.sayHi нашего объекта.
Затем мы вызвали её. Теперь пользователь может говорить!
Функцию, которая является свойством объекта, называют методом этого объекта.
Итак, мы получили метод sayHi объекта user .
Конечно, мы могли бы заранее объявить функцию и использовать её в качестве метода, примерно так:
Когда мы пишем наш код, используя объекты для представления сущностей реального мира, – это называется объектно-ориентированное программирование или сокращённо: «ООП».
ООП является большой предметной областью и интересной наукой само по себе. Как выбрать правильные сущности? Как организовать взаимодействие между ними? Это – создание архитектуры, и есть хорошие книги по этой теме, такие как «Приёмы объектно-ориентированного проектирования. Паттерны проектирования» авторов Эрих Гамма, Ричард Хелм, Ральф Джонсон, Джон Влиссидес или «Объектно-ориентированный анализ и проектирование с примерами приложений» Гради Буча, а также ещё множество других книг.
Сокращённая запись метода
Существует более короткий синтаксис для методов в литерале объекта:
Как было показано, мы можем пропустить ключевое слово "function" и просто написать sayHi() .
Нужно отметить, что эти две записи не полностью эквивалентны. Есть тонкие различия, связанные с наследованием объектов (что будет рассмотрено позже), но на данном этапе изучения это неважно. В большинстве случаев сокращённый синтаксис предпочтителен.
Ключевое слово «this» в методах
Как правило, методу объекта необходим доступ к информации, которая хранится в объекте, чтобы выполнить с ней какие-либо действия (в соответствии с назначением метода).
Например, коду внутри user.sayHi() может понадобиться имя пользователя, которое хранится в объекте user .
Для доступа к информации внутри объекта метод может использовать ключевое слово this .
Значение this – это объект «перед точкой», который использовался для вызова метода.
Здесь во время выполнения кода user.sayHi() значением this будет являться user (ссылка на объект user ).
Технически также возможно получить доступ к объекту без ключевого слова this , ссылаясь на него через внешнюю переменную (в которой хранится ссылка на этот объект):
Это показано ниже:
Если мы используем this.name вместо user.name внутри alert , тогда этот код будет работать.
«this» не является фиксированным
В JavaScript ключевое слово «this» ведёт себя иначе, чем в большинстве других языков программирования. Оно может использоваться в любой функции.
В этом коде нет синтаксической ошибки:
Значение this вычисляется во время выполнения кода и зависит от контекста.
Например, здесь одна и та же функция назначена двум разным объектам и имеет различное значение «this» при вызовах:
Правило простое: при вызове obj.f() значение this внутри f равно obj . Так что, в приведённом примере это user или admin .
Мы даже можем вызвать функцию вовсе без использования объекта:
В строгом режиме ( "use strict" ) в таком коде значением this будет являться undefined . Если мы попытаемся получить доступ к name , используя this.name – это вызовет ошибку.
В нестрогом режиме значением this в таком случае будет глобальный объект ( window для браузера, мы вернёмся к этому позже в главе Глобальный объект). Это – исторически сложившееся поведение this , которое исправляется использованием строгого режима ( "use strict" ).
Обычно подобный вызов является ошибкой программирования. Если внутри функции используется this , тогда ожидается, что она будет вызываться в контексте какого-либо объекта.
Если вы до этого изучали другие языки программирования, тогда вы, скорее всего, привыкли к идее "фиксированного this " – когда методы, определённые внутри объекта, всегда сохраняют в качестве значения this ссылку на свой объект (в котором был определён метод).
В JavaScript this является «свободным», его значение вычисляется в момент вызова метода и не зависит от того, где этот метод был объявлен, а зависит от того, какой объект вызывает метод (какой объект стоит «перед точкой»).
Эта идея вычисления this в момент исполнения имеет как свои плюсы, так и минусы. С одной стороны, функция может быть повторно использована в качестве метода у различных объектов (что повышает гибкость). С другой стороны, большая гибкость увеличивает вероятность ошибок.
Здесь мы не будем судить о том, является ли это решение в языке хорошим или плохим. Мы должны понимать, как с этим работать, чтобы получать выгоды и избегать проблем.
Внутренняя реализация: Ссылочный тип
Этот раздел объясняет сложную тему, чтобы лучше понимать некоторые запутанные случаи.
Если вы хотите продвигаться быстрее, его можно пропустить или отложить.
Некоторые хитрые способы вызова метода приводят к потере значения this , например:
В последней строчке кода используется условный оператор ? , который определяет, какой будет вызван метод ( user.hi или user.bye ) в зависимости от выполнения условия. В данном случае будет выбран user.hi .
Затем метод тут же вызывается с помощью скобок () . Но вызов не работает как положено!
Вы можете видеть, что при вызове будет ошибка, потому что значением "this" внутри функции становится undefined (полагаем, что у нас строгий режим).
Так работает (доступ к методу объекта через точку):
Так уже не работает (вызываемый метод вычисляется):
Почему? Если мы хотим понять, почему так происходит, давайте разберёмся (заглянем под капот), как работает вызов методов ( obj.method() ).
Присмотревшись поближе, в выражении obj.method() можно заметить две операции:
- Сначала оператор точка '.' возвращает свойство объекта – его метод ( obj.method ).
- Затем скобки () вызывают этот метод (исполняется код метода).
Итак, каким же образом информация о this передаётся из первой части во вторую?
Если мы поместим эти операции в отдельные строки, то значение this , естественно, будет потеряно:
Здесь hi = user.hi сохраняет функцию в переменной, и далее в последней строке она вызывается полностью сама по себе, без объекта, так что нет this .
Для работы вызовов типа user.hi() , JavaScript использует трюк – точка '.' возвращает не саму функцию, а специальное значение «ссылочного типа», называемого Reference Type.
Этот ссылочный тип (Reference Type) является внутренним типом. Мы не можем явно использовать его, но он используется внутри языка.
Значение ссылочного типа – это «триплет»: комбинация из трёх значений (base, name, strict) , где:
- base – это объект.
- name – это имя свойства объекта.
- strict – это режим исполнения. Является true, если действует строгий режим ( use strict ).
Результатом доступа к свойству user.hi является не функция, а значение ссылочного типа. Для user.hi в строгом режиме оно будет таким:
Когда скобки () применяются к значению ссылочного типа (происходит вызов), то они получают полную информацию об объекте и его методе, и могут поставить правильный this ( =user в данном случае, по base ).
Ссылочный тип – исключительно внутренний, промежуточный, используемый, чтобы передать информацию от точки . до вызывающих скобок () .
При любой другой операции, например, присваивании hi = user.hi , ссылочный тип заменяется на собственно значение user.hi (функцию), и дальше работа уже идёт только с ней. Поэтому дальнейший вызов происходит уже без this .
Таким образом, значение this передаётся правильно, только если функция вызывается напрямую с использованием синтаксиса точки obj.method() или квадратных скобок obj['method']() (они делают то же самое). Позднее в этом учебнике мы изучим различные варианты решения проблемы потери значения this . Например, такие как func.bind().
У стрелочных функций нет «this»
Стрелочные функции особенные: у них нет своего «собственного» this . Если мы используем this внутри стрелочной функции, то его значение берётся из внешней «нормальной» функции.
Например, здесь arrow() использует значение this из внешнего метода user.sayHi() :
Это является особенностью стрелочных функций. Они полезны, когда мы на самом деле не хотим иметь отдельное значение this , а хотим брать его из внешнего контекста. Позднее в главе Повторяем стрелочные функции мы увидим больше примеров на эту тему.
Итого
- Функции, которые находятся в объекте в качестве его свойств, называются «методами».
- Методы позволяют объектам «действовать»: object.doSomething() .
- Методы могут ссылаться на объект через this .
Значение this определяется во время исполнения кода.
- При объявлении любой функции в ней можно использовать this , но этот this не имеет значения до тех пор, пока функция не будет вызвана.
- Эта функция может быть скопирована между объектами (из одного объекта в другой).
- Когда функция вызывается синтаксисом «метода» – object.method() , значением this во время вызова является объект перед точкой.
Также ещё раз заметим, что стрелочные функции являются особенными – у них нет this . Когда внутри стрелочной функции обращаются к this , то его значение берётся снаружи.
Обычный синтаксис <. >позволяет создать только один объект. Но зачастую нам нужно создать множество однотипных объектов, таких как пользователи, элементы меню и т.д.
Это можно сделать при помощи функции-конструктора и оператора "new" .
Функция-конструктор
Функции-конструкторы являются обычными функциями. Но есть два соглашения:
- Имя функции-конструктора должно начинаться с большой буквы.
- Функция-конструктор должна вызываться при помощи оператора "new" .
Когда функция вызывается как new User(. ) , происходит следующее:
- Создаётся новый пустой объект, и он присваивается this .
- Выполняется код функции. Обычно он модифицирует this , добавляет туда новые свойства.
- Возвращается значение this .
Другими словами, вызов new User(. ) делает примерно вот что:
То есть, результат вызова let user = new User("Вася") – это тот же объект, что и:
Теперь, когда нам необходимо будет создать других пользователей, мы можем использовать new User("Маша") , new User("Даша") и т.д. Данная конструкция гораздо удобнее и читабельнее, чем каждый раз создавать литерал объекта. Это и является основной целью конструкторов – удобное повторное создание однотипных объектов.
Ещё раз заметим: технически любая функция может быть использована как конструктор. То есть, каждая функция может быть вызвана при помощи оператора new , и выполнится алгоритм, указанный выше в примере. Заглавная буква в названии функции является всеобщим соглашением по именованию, она как бы подсказывает разработчику, что данная функция является функцией-конструктором, и её нужно вызывать через new .
Если в нашем коде большое количество строк, создающих один сложный объект, мы можем обернуть их в функцию-конструктор следующим образом:
Такой конструктор не может быть вызван дважды, так как он нигде не сохраняется, просто создаётся и тут же вызывается. Таким образом, такой метод создания позволяет инкапсулировать код, который создаёт отдельный объект, но без возможности его повторного использования.
Проверка на вызов в режиме конструктора: new.target
Данный метод используется очень редко. Вы можете пропустить эту секцию, если не хотите углубляться в детали языка.
Используя специальное свойство new.target внутри функции, мы можем проверить, вызвана ли функция при помощи оператора new или без него.
В случае, если функция вызвана при помощи new , то в new.target будет сама функция, в противном случае undefined .
Это можно использовать, чтобы отличить обычный вызов от вызова «в режиме конструктора». В частности, вот так можно сделать, чтобы функцию можно было вызывать как с, так и без new :
Такой подход иногда используется в библиотеках для создания более гибкого синтаксиса, который позволяет разработчикам вызывать функции при помощи оператора new или без него.
Впрочем, это не очень хорошая практика, так как отсутствие new может ввести разработчика в заблуждение. С оператором new мы точно знаем, что в итоге будет создан новый объект.
Возврат значения из конструктора return
Обычно конструкторы ничего не возвращают явно. Их задача – записать все необходимое в this , который в итоге станет результатом.
Но если return всё же есть, то применяется простое правило:
- При вызове return с объектом, будет возвращён объект, а не this .
- При вызове return с примитивным значением, примитивное значение будет отброшено.
Другими словами, return с объектом возвращает объект, в любом другом случае конструктор вернёт this .
Считается хорошей практикой программирования, чтобы название функции конструкторов начиналось с большой буквы.
Объектные типы (Шаблоны) (Классы)
Примеры из предыдущих глав значительно ограничены. В них создается только одиночные объекты.
Тем не менее, иногда требуется иметь некий "шаблон", по которому можно было бы создавать множество объектов одного и того же "типа".
Для создания "объектного типа" и используется функция конструктора объекта.
В приведенном в начале этой главы примере функция Person() является функцией конструктора объекта.
Объекты одного и того же типа создаются при помощи вызова функции конструктора с ключевым словом new:
Ключевое слово this
В JavaScript ключевое слово this обозначает объект, которому "принадлежит" данный код.
Значением ключевого слова this, когда оно используется в объекте, является сам объект.
В функции конструктора у ключевого слова this нет значения. Это "подстановка" для нового объекта. Когда будет создан новый объект, тогда значением ключевого слова this и станет этот новый объект.
Обратите внимание, что this это не переменная, а ключевое слово. Вы не можете изменять его значение.
Добавление свойства к объекту
Добавить новое свойство к существующему объекту очень просто:
Свойство будет добавлено к объекту myFather, но не к объекту myMother. (Или какому-либо другому объекту типа person).
Добавление метода к объекту
Добавить новый метод к существующему объекту очень просто:
Метод будет добавлен к объекту myFather, но не к объекту myMother. (Или какому-либо другому объекту типа person).
Добавление свойства к конструктору объекта
Нельзя добавлять новое свойство к конструктору объекта тем же способом, как это делается в случае с существующим объектом.
Чтобы добавить новое свойство к конструктору, вы должны добавить его в функцию конструктора:
При этом свойствам объекта можно устанавливать значения по умолчанию.
Добавление метода к конструктору объекта
Функция конструктора также может определять методы:
Нельзя добавлять новые методы к конструктору объекта тем же способом, как это делается в случае с существующим объектом. Добавление методов к объекту должно происходить внутри функции конструктора:
Функция changeName() присваивает значение параметра name свойству lastName объекта person:
JavaScript знает, о каком объекте идет речь, "подставляя" в ключевое слово this объект myMother.
Встроенные конструкторы JavaScript
В JavaScript есть встроенные конструкторы для собственных объектов:
В этом списке нет объекта Math(), так как это глобальный объект. Ключевое слово new нельзя использовать с объектом Math.
А вы знали?
Как видно из приведенного выше кода, в JavaScript есть объектные версии примитивных типов данных String, Number и Boolean. Однако нет никаких причин создавать для этих типов комплексные объекты. Примитивные значения работают быстрее.
Код внутри функции JavaScript не выполняется, когда эта функция определяется. Он выполняться только тогда, когда эта функция будет "запущена".
Обычно говорят "вызвать функцию" или "выполнить функцию".
Запуск функции как функции
Функция в предыдущем примере не принадлежит какому-либо объекту. Однако в JavaScript всегда существует "дефолтный" глобальный объект.
В HTML таким глобальным объектом является HTML страница, таким образом, функция из предыдущего примера "принадлежит" текущей HTML странице.
В браузере объект страницы — это окно браузера или объект window. Так, функция из примера автоматически становится функцией объекта window. Таким образом, myFunction() и window.myFunction() это одна и та же функция:
Это обычный способ вызывать функции JavaScript. Однако, это не очень хорошая практика. Глобальные переменные, методы или функции могут создавать конфликты имен и приводить к сбоям в глобальном объекте.
Ключевое слово this
В JavaScript ключевое слово this — это объект, который "владеет" текущим кодом.
При использовании в функции значением ключевого слова this является объект, который "владеет" данной функцией.
Обратите внимание, что this — это не переменная, а ключевое слово. Вы не можете изменять его значение.
Глобальный объект
Когда функция вызывается без объекта-владельца, значением ключевого слова this становится глобальный объект.
В браузере глобальный объект — это окно браузера (объект window).
В следующем примере функция возвращает объект window:
Внимание! Вызов функции как глобальной, устанавливает this на глобальный объект. Использование объекта window в качестве переменной может привести к краху вашей программы.
Вызов функции как метода
В JavaScript вы можете определять функции как методы объектов.
В следующем примере создается объект (myObject) с двумя свойствами (firstName и lastName) и одним методом (fullName):
Метод fullName — функция. Эта функция принадлежит объекту. Объект myObject — владелец функции fullName.
Ключевое слово this указывает на объект, который "владеет" данным кодом JavaScript. В данном случае значением ключевого слова this является объект myObject:
Вызов функции как метод объекта устанавливает в качестве значения ключевого слова this сам объект.
Вызов функции как конструктора объекта
Если перед вызовом функции стоит ключевое слово new, то это вызывается конструктор объекта.
Выглядит так, будто вы создаете новую функцию, но так как функции в JavaScript являются объектами, вы на самом деле создаете новый объект:
Вызов конструктора создает новый объект. Новый объект наследует свойства и методы своего конструктора.
У ключевого слова this в конструкторе нет значения. Значением ключевого слова this станет новый объект, созданный при вызове функции конструктора.
Читайте также: