Методы объекта, "this"
Объекты обычно создаются, чтобы представлять сущности реального мира, будь то пользователи, заказы и так далее:
И так же, как и в реальном мире, пользователь может совершать действия: выбирать что-то из корзины покупок, авторизовываться, выходить из системы, оплачивать и т.п.
Такие действия в JavaScript представлены свойствами-функциями объекта.
Примеры методов
Для начала давайте научим нашего пользователя user здороваться:
Здесь мы просто использовали Function Expression (функциональное выражение), чтобы создать функцию для приветствия, и присвоили её свойству user.sayHi нашего объекта.
Затем мы вызвали её. Теперь пользователь может говорить!
Функцию, которая является свойством объекта, называют методом этого объекта.
Итак, мы получили метод sayHi объекта user .
Конечно, мы могли бы заранее объявить функцию и использовать её в качестве метода, примерно так:
Когда мы пишем наш код, используя объекты для представления сущностей реального мира, – это называется объектно-ориентированное программирование или сокращённо: «ООП».
ООП является большой предметной областью и интересной наукой само по себе. Как выбрать правильные сущности? Как организовать взаимодействие между ними? Это – создание архитектуры, и есть хорошие книги по этой теме, такие как «Приёмы объектно-ориентированного проектирования. Паттерны проектирования» авторов Эрих Гамма, Ричард Хелм, Ральф Джонсон, Джон Влиссидес или «Объектно-ориентированный анализ и проектирование с примерами приложений» Гради Буча, а также ещё множество других книг.
Сокращённая запись метода
Существует более короткий синтаксис для методов в литерале объекта:
Как было показано, мы можем пропустить ключевое слово "function" и просто написать sayHi() .
Нужно отметить, что эти две записи не полностью эквивалентны. Есть тонкие различия, связанные с наследованием объектов (что будет рассмотрено позже), но на данном этапе изучения это неважно. В большинстве случаев сокращённый синтаксис предпочтителен.
Ключевое слово «this» в методах
Как правило, методу объекта необходим доступ к информации, которая хранится в объекте, чтобы выполнить с ней какие-либо действия (в соответствии с назначением метода).
Например, коду внутри user.sayHi() может понадобиться имя пользователя, которое хранится в объекте user .
Для доступа к информации внутри объекта метод может использовать ключевое слово this .
Значение this – это объект «перед точкой», который использовался для вызова метода.
Здесь во время выполнения кода user.sayHi() значением this будет являться user (ссылка на объект user ).
Технически также возможно получить доступ к объекту без ключевого слова this , ссылаясь на него через внешнюю переменную (в которой хранится ссылка на этот объект):
…Но такой код будет ненадёжным. Если мы решим скопировать ссылку на объект user в другую переменную, например, admin = user , и перезапишем переменную user чем-то другим, тогда будет осуществлён доступ к неправильному объекту при вызове метода из admin .
Это показано ниже:
Если мы используем 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 , то его значение берётся снаружи.
Как вызвать два метода подряд через точку?
![]()
Реализуется при помощи возврата экземпляра того же класса из методов:
После это используется так:
![]()
Для вызова метода чем точку, предыдущий метод должен возвращать объект того же класса.
Затем создаете объект и вызываете методы:
С полным примером, хотя не совсем понял, что у вас с модификаторами доступа. Объект класса в любом случае придется создавать,ибо в таком синтактисе нужна ссылка на не статический класс
Если необходимо, могу привести немного другой пример работы с объектом и таким синтаксисом
![]()
Всё ещё ищете ответ? Посмотрите другие вопросы с метками java ооп методы или задайте свой вопрос.
Site design / logo © 2022 Stack Exchange Inc; user contributions licensed under cc by-sa. rev 2022.6.10.42345
Нажимая «Принять все файлы cookie», вы соглашаетесь, что Stack Exchange может хранить файлы cookie на вашем устройстве и раскрывать информацию в соответствии с нашей Политикой в отношении файлов cookie.
Полезные методы массивов и объектов в JavaScript
Автор статьи, перевод которой мы сегодня публикуем, говорит, что её идею подсказал ему один из выпусков подкаста Syntax FM, в котором давался обзор полезных методов объектов и массивов в JavaScript. Эти методы помогают разработчикам писать чистый и читабельный код. Их применение снижает потребность в сторонних библиотеках наподобие Lodash.
Array.prototype.filter()
Метод Array.prototype.filter() создаёт новый массив, в который попадают только те элементы исходного массива, которые соответствуют заданному условию.
▍Пример
Создадим на основе массива, содержащего сведения о студентах, новый массив, в который попадут записи только о тех студентах, возраст которых позволяет им покупать спиртные напитки.
Array.prototype.map()
Метод Array.prototype.map() позволяет создать новый массив, основанный на каким-то образом обработанных значениях другого массива. Этот метод отлично подходит для модификации данных, он, благодаря тому, что не вносит изменений в исходный массив, часто используется в React.
▍Пример
Создадим, на основе числового массива, новый массив, в начале каждого элемента которого будет помещён знак $ .
Array.prototype.reduce()
Метод Array.prototype.reduce() нередко незаслуженно обходят вниманием. Он позволяет свести массив к единственному значению, накапливаемому в элементе-приёмнике. Значение, возвращаемое этим методом, может быть любого типа. Например, это может быть объект, массив, строка или число.
▍Пример
Воспользуемся методом .reduce() для того, чтобы получить сумму элементов числового массива.
На самом деле, этот метод можно использовать множеством интереснейших способов. Соответствующие примеры можно найти в документации MDN. В частности, речь идёт о разворачивании массивов, состоящих из массивов, о группировке объектов по свойствам, и об удалении из массивов повторяющихся элементов.
Array.prototype.forEach()
Метод Array.prototype.forEach() применяет переданную ему функцию к каждому элементу массива.
▍Пример
Вот как вывести каждый элемент массива в консоль с помощью метода .forEach() .
Array.prototype.some()
Метод Array.prototype.some() проверяет, соответствует ли хотя бы один элемент массива условию, задаваемому передаваемой ему функцией. Этот метод, например, способен хорошо показать себя в решении задачи проверки полномочий пользователя. Его можно рассматривать в качестве аналога ранее рассмотренного .forEach() , с той разницей, что, при его применении, с помощью функции, которая ему передана, можно выполнять над элементами массива некие действия до тех пор, пока эта функция не вернёт истинное значение, после чего — прервать обработку.
▍Пример
Проверим, имеется ли в массиве как минимум одна строка admin .
Array.prototype.every()
Метод Array.prototype.every() похож на вышеописанный метод .some() , но он возвращает true только в том случае, если все элементы массива соответствуют условию, задаваемому передаваемой этому методу функцией.
▍Пример
Проверим, все ли оценки, хранящиеся в массиве, равны или больше чем 3.
Array.prototype.includes()
Метод Array.prototype.includes() позволяет узнать, содержит ли массив заданное значение. Он похож на метод .some() , но он проверяет не соответствие элементов массива некоему условию, а наличие в массиве заданного при его вызове значения.
▍Пример
Проверим, имеется ли в массиве строковой элемент waldo :
Array.from()
Статический метод Array.from() позволяет создавать массивы на основании других массивов или строк. Этому методу, при необходимости, можно передать функцию для выполнения мэппинга, что позволяет воздействовать на данные, которые попадут в новый массив. На самом деле, для выполнения мэппинга предусмотрен особый метод — Array.prototype.map(), поэтому не вполне понятно, зачем кому-то может понадобиться эта возможность метода Array.from() .
▍Пример
Создадим массив на основе строки.
Создадим массив, содержащий удвоенные значения элементов исходного числового массива.
Object.values()
Метод Object.values() возвращает массив значений свойств переданного ему объекта.
▍Пример
Сформируем массив из значений свойств объекта.
Object.keys()
Метод Object.keys() возвращает массив, состоящий из имён свойств объекта (ключей).
▍Пример
Сформируем массив из имён свойств объекта.
Object.entries()
Метод Object.entries() возвращает, обработав переданный ему объект, массив, содержащий массивы, представляющие собой пары вида [ключ, значение] , представляющие собой имена свойств и значения этих свойств.
▍Пример
Сформируем массив, содержащий, для интересующего нас объекта, данные об именах свойств и их значениях.
Оператор расширения и массивы
Оператор расширения (spread operator, …), в применении к массивам, позволяет разворачивать их, извлекая из них их элементы. Этот оператор полезен, например, при необходимости выполнения объединения нескольких массивов. Кроме того, его использование позволяет избавиться от необходимости использовать метод .splice() в том случае, если нужно удалить из массива какие-то элементы, так как он может быть скомбинирован с методом .slice() , что позволяет избежать изменения исходного массива.
▍Пример
Объединим два массива.
Сформируем новый массив, представляющий собой исходный массив, из которого удалён элемент. При этом исходный массив меняться не должен.
Оператор расширения и объекты
Применение оператора расширения к объектам позволяет добавлять к ним новые свойства и значения без изменения исходных объектов (то есть, в результате подобных операций создаются новые объекты). Кроме того, этот оператор можно использовать для объединения объектов. Тут стоит отметить, что оператор расширения, применённый к объекту, не воздействует на объекты, вложенные в него.
▍Пример
Создадим новый объект, добавив к исходному объекту новое свойство, не меняя при этом исходный объект.
Синтаксис оставшихся параметров функции
При работе с функциями можно использовать синтаксис оставшихся параметров для того, чтобы организовать приём любого количества аргументов в виде массива.
▍Пример
Выведем массив, содержащий аргументы, переданные функции.
Object.freeze()
Метод Object.freeze() «замораживает» объект, предотвращая изменение существующих свойств этого объекта или добавление новых свойств и значений в объект. Существует ошибочное мнение, что этот метод аналогичен объявлению объектов с использованием ключевого слова const , однако, объекты-константы модифицировать можно.
▍Пример
«Заморозим» объект, после чего попытаемся изменить его свойство и убедимся в том, что сделать этого нельзя.
Object.seal()
Метод Object.seal() позволяет «запечатать» объект, предотвратив добавление новых свойств. При этом существующие свойства можно менять.
▍Пример
«Запечатаем» объект, что не позволит добавить в него новое свойство, но оставит возможность менять существующие свойства.
Object.assign()
Метод Object.assign() позволяет объединять объекты, копируя свойства из одного объекта в другой. На самом деле, того же эффекта можно достичь с помощью вышеописанного оператора расширения, поэтому без этого метода вполне можно обойтись. Надо отметить, что этот метод, как и оператор расширения, не выполняет глубокого клонирования объектов. Если вам нужно готовое средство для глубокого клонирования объектов — взгляните на инструменты библиотеки Lodash.
▍Пример
Создадим из двух объектов один.
Итоги
В этом материале мы рассказали о полезных методах массивов и объектов в JavaScript. Многие рассмотренные здесь методы возвращают новые массивы или объекты. Это даёт возможность комбинировать их, объединяя в цепочки. Эти методы не модифицируют исходные данные, что особенно важно в React-разработке. Кроме того, использование этих методов, в подавляющем большинстве случаев, позволяет отказаться от циклов вроде for и while .
Уважаемые читатели! Какими методами массивов и объектов в JavaScript вы пользуетесь чаще всего?