Для чего необходимо ключевое слово this
Перейти к содержимому

Для чего необходимо ключевое слово this

Ключевое слово this в JavaScript для начинающих

Автор материала, перевод которого мы сегодня публикуем, говорит, что когда она работала в сфере бухучёта, там применялись понятные термины, значения которых легко найти в словаре. А вот занявшись программированием, и, в частности, JavaScript, она начала сталкиваться с такими понятиями, определения которых в словарях уже не найти. Например, это касается ключевого слова this . Она вспоминает то время, когда познакомилась с JS-объектами и функциями-конструкторами, в которых использовалось это ключевое слово, но добраться до его точного смысла оказалось не так уж и просто. Она полагает, что подобные проблемы встают и перед другими новичками, особенно перед теми, кто раньше программированием не занимался. Тем, кто хочет изучить JavaScript, в любом случае придётся разобраться с this . Этот материал направлен на то, чтобы всем желающим в этом помочь.

Что такое this?

Предлагаю вашему вниманию моё собственное определение ключевого слова this . This — это ключевое слово, используемое в JavaScript, которое имеет особое значение, зависящее от контекста в котором оно применяется.

Причина, по которой this вызывает столько путаницы у новичков, заключается в том, что контекст this меняется в зависимости от его использования.

This можно считать динамическим ключевым словом. Мне нравится, как понятие «контекст» раскрыто в этой статье Райана Морра. По его словам, контекст всегда является значением ключевого слова this , которое ссылается на объект, «владеющий» кодом, выполняемым в текущий момент. Однако, тот контекст, который имеет отношение к this , это не то же самое, что контекст выполнения.

Итак, когда мы пользуемся ключевым словом this , мы, на самом деле, обращаемся с его помощью к некоему объекту. Поговорим о том, что это за объект, рассмотрев несколько примеров.

Ситуации, когда this указывает на объект window

Если вы попытаетесь обратиться к ключевому слову this в глобальной области видимости, оно будет привязано к глобальному контексту, то есть — к объекту window в браузере.

При использовании функций, которые имеются в глобальном контексте (это отличает их от методов объектов) ключевое слово this в них будет указывать на объект window .

Попробуйте выполнить этот код, например, в консоли браузера:

Использование this внутри объекта

Когда this используется внутри объекта, это ключевое слово ссылается на сам объект. Рассмотрим пример. Предположим, вы создали объект dog с методами и обратились в одном из его методов к this . Когда this используется внутри этого метода, это ключевое слово олицетворяет объект dog .

This и вложенные объекты

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

Особенности стрелочных функций

Стрелочные функции ведут себя не так, как обычные функции. Вспомните: при обращении к this в методе объекта, этому ключевому слову соответствует объект, которому принадлежит метод. Однако это не относится к стрелочным функциям. Вместо этого, this в таких функциях относится к глобальному контексту (к объекту window ). Рассмотрим следующий код, который можно запустить в консоли браузера.

Если, озадачившись рассматриваемым вопросом, заглянуть на MDN, там можно найти сведения о том, что стрелочные функции имеют более короткую форму записи, чем функциональные выражения и не привязаны к собственным сущностям this , arguments , super или new.target . Стрелочные функции лучше всего подходят для использования их в роли обычных функций, а не методов объектов, их нельзя использовать в роли конструкторов.

Прислушаемся к MDN и не будем использовать стрелочные функции в качестве методов объектов.

Использование this в обычных функциях

Когда обычная функция находится в глобальной области видимости, то ключевое слово this , использованное в ней, будет привязано к объекту window . Ниже приведён пример, в котором функцию test можно рассматривать в виде метода объекта window .

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

Обращение к this из функции, которая была объявлена за пределами объекта, а потом назначена в качестве его метода

Рассмотрим пример с уже известным нам объектом dog . В качестве метода этого объекта можно назначить функцию chase , объявленную за его пределами. Тут в объекте dog никаких методов не было, до тех пор, пока мы не создали метод foo , которому назначена функция chase . Если теперь вызвать метод dog.foo , то будет вызвана функция chase . При этом ключевое слово this , к которому обращаются в этой функции, указывает на объект dog . А функция chase , при попытке её вызова как самостоятельной функции, будет вести себя неправильно, так как при таком подходе this будет указывать на глобальный объект, в котором нет тех свойств, к которым мы, в этой функции, обращаемся через this .

Ключевое слово new и this

Ключевое слово this находит применение в функциях-конструкторах, используемых для создания объектов, так как оно позволяет, универсальным образом, работать со множеством объектов, создаваемых с помощью такой функции. В JavaScript есть и стандартные функции-конструкторы, с помощью которых, например, можно создавать объекты типа Number или String . Подобные функции, определяемые программистом самостоятельно, позволяют ему создавать объекты, состав свойств и методов которых задаётся им самим.

Как вы уже поняли, мне нравятся собаки, поэтому опишем функцию-конструктор для создания объектов типа Dog , содержащих некоторые свойства и методы.

Когда функцию-конструктор вызывают с использованием ключевого слова new , this в ней указывает на новый объект, который, с помощью конструктора, снабжают свойствами и методами.

Вот как можно работать со стандартными конструкторами JavaScript.

Теперь поработаем с только что созданной функцией-конструктором Dog .

Вот ещё один пример использования функций-конструкторов.

О важности ключевого слова new

При вызове функции-конструктора с использованием ключевого слова new ключевое слово this указывает на новый объект, который, после некоторой работы над ним, будет возвращён из этой функции. Ключевое слово this в данной ситуации весьма важно. Почему? Всё дело в том, что с его помощью можно, используя единственную функцию-конструктор, создавать множество однотипных объектов.

Это позволяет нам масштабировать приложение и сокращать дублирование кода. Для того чтобы понять важность этого механизма, подумайте о том, как устроены учётные записи в социальных сетях. Каждая учётная запись может представлять собой экземпляр объекта, создаваемый с помощью функции-конструктора Friend . Каждый такой объект можно заполнять уникальными данными о пользователе. Рассмотрим следующий код.

Итоги

На самом деле, особенности использования ключевого слова this в JavaScript не ограничиваются вышеописанными примерами. Так, в череду этих примеров можно было бы включить использование функций call , apply и bind . Так как материал этот рассчитан на начинающих и ориентирован на разъяснение основ, мы их здесь не касаемся. Однако если сейчас у вас сформировалось начальное понимание this , то и с этими методами вы вполне сможете разобраться. Главное — помните о том, что если что-то с первого раза понять не удаётся, не прекращайте учиться, практикуйтесь, читайте материалы по интересующей вас теме. В одном из них вам обязательно попадётся нечто такое (какая-то удачная фраза, например), что поможет понять то, что раньше понять не удавалось.

Уважаемые читатели! Возникали ли у вас сложности с пониманием ключевого слова this в JavaScript?

Методы объекта, "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() можно заметить две операции:

  1. Сначала оператор точка ‘.’ возвращает свойство объекта – его метод ( obj.method ).
  2. Затем скобки () вызывают этот метод (исполняется код метода).

Итак, каким же образом информация о 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 , то его значение берётся снаружи.

Для чего необходимо ключевое слово this

Ключевое слово this в JavaScript , так же, как и во многих других языках программирования, используется подобно местоимению в естественном языке. Так же, как в естественном языке, с помощью местоимений мы указываем на определенного человека, животное, предмет, в JavaScript мы можем использовать this для указания на определенный объект.

Неаккуратное использование this в JavaScript может стать источником серьезных и трудно отлавливаемых ошибок, что нередко приводит в замешательство начинающих JavaScript-программистов.

Эта статья не для гуру JS’а, вы не найдете здесь ничего нового. Если же вы только знакомитесь с этим замечательным языком — добро пожаловать в кроличью нору 😉 В статье мы попытаемся немного пролить свет на особенности использования ключевого слова this, а также рассмотрим несколько щекотливых ситуаций, когда использование this вызывает наибольшие трудности.

Основы использования ключевого слова this

Для начала рассмотрим пример использования this в JavaScript:

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

Преимущество this очевидно: если мы в будущем захотим, скажем, переименовать объект user, при использовании второго варианта нам придется изменять его имя во всех подобных методах. Таким образом, использование this делает наш код более эстетичным и чистым: подобно местоимению, ключевое слово this можно использовать как указатель на тот объект, в контексте которого выполняется функция.

Что же такое this? Переменная this в функции всегда ссылается на объект, который вызывает эту функцию как метод. Если же функция вызывается просто как функция, а не как метод какого-либо объекта — значение this ссылается на глобальный объект (в строгом режиме в этом случае значением this будет undefined).

Таким образом, переменная this, используемая внутри функции (скажем, функции A), содержит ссылку на объект, который вызывает функцию А. Т.е. переменная this позволяет получить доступ к свойствам и методам объекта, вызывающего функцию A. Это особенно актуально в том случае, если мы не всегда знаем, какой объект вызывает функцию. Таким образом, this — это своего рода ярлык внутри функции, ссылка на вызывающий функцию объект.

Простейший пример использования this в JavaScript:

Самое главное, что нужно помнить при использовании ключевого слова this: this не присваивается никакого значения до тех пор, пока функция не вызвана.

Использование this в глобальной области видимости

В глобальном контексте, если код выполняется в браузере, все переменные и функции добавляются к глобальному объекту window. Поэтому при вызове функции в глобальном контексте значением переменной this будет ссылка на глобальный объект window (кроме режима strict mode, об этом упоминалось ранее).

Рассмотрим этот момент на примере:

В данном примере, вызывая глобальную функцию showName(), мы обращаемся к объекту window и пытаемся получить значение переменной name.

Обратите внимание, если в начале примера написать строчку «use strict»; — то при вызове глобальной функции showName() возникнет ошибка, т.к. значением переменной this будет undefined.

Когда использовать this следует наиболее осторожно

Использование this становится не совсем очевидным:

  • при использовании метода в качестве функции обратного вызова;
  • при использовании this внутри замыкания — во внутренней функции;
  • при присваивании метода;
  • при заимствовании метода.

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

Использование метода в качестве функции обратного вызова

Один из тонких моментов при использовании ключевого слова this — передача метода, использующего this в качестве callback’а другой функции.

В этом примере у нас есть объект users c методом clickHandler, использующим this. При использовании этого метода, мы предполагаем, что объектом this в данном случае будет выступать объект users. Но когда мы передаем этот метод в качестве функции обратного вызова — значением объекта this будет объект кнопки. Этот момент не вполне очевиден, однако вызывает неприятные ошибки.

Если мы хотим, чтобы this.data действительно указывал на свойство data объекта users — мы должны использовать один из методов bind(), apply() или call() как раз для того, чтобы явно задать значение this. Т.е. вместо:

Использование this внутри замыкания

Другой случай, когда могут возникать проблемы с использованием this — когда мы используем внутреннюю функцию (замыкание). Здесь важно иметь в виду, что замыкание не может получить доступ к значению this внешней функции (в отличии от других переменных внешней функции), т.к. имеет собственное значение this.

В данном примере мы опять-таки ожидаем, что значением this будет объект users — однако внутри замыкания свое значение this, в данном примере — глобальный объект window. Значением this.helloText будет undefined.

Как избежать ошибки при использовании this в замыканиях? Очень просто — проинициализировать значением this другую переменную во внешней функции и использовать ее в замыкании:

Присваивание метода переменной

Когда мы присваиваем метод переменной — this снова ведет себя не совсем так, как мы ожидаем.

В данном случае, когда мы вызываем функцию showFirstUser() значением this будет глобальный объект window, соответственно, значение data будет взято из глобальной области видимости.

Решением данной проблемы может быть использование метода bind() для явного указания значения this:

Использование this при заимствовании методов

Заимствование методов — это довольно распространенная, экономящая время практика среди JavaScript-разработчиков. Давайте рассмотрим на примере проблемы, которые могут возникать c this при заимствовании метода:

В данном примере функция manager.getAge() вызывается в контексте объекта manager, соответственно значение свойства age будет установлено у объекта manager.

Чтобы избежать подобных проблем, можно воспользоваться методом apply():

Заключение

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

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

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *