- Методы объекта, «this»
- Примеры методов
- Сокращённая запись метода
- Ключевое слово «this» в методах
- «this» не является фиксированным
- У стрелочных функций нет «this»
- Итого
- Задачи
- Использование «this» в литерале объекта
- The JavaScript this Keyword
- Note
- this in a Method
- this Alone
- Example
- Example
- this in a Function (Default)
- Example
- this in a Function (Strict)
- Example
- this in Event Handlers
- Example
- Object Method Binding
- Example
- Example
- Explicit Function Binding
- See Also:
- Example
- Function Borrowing
- Example
- This Precedence
Методы объекта, «this»
Объекты обычно создаются, чтобы представлять сущности реального мира, будь то пользователи, заказы и так далее:
// Объект пользователя let user = < name: "John", age: 30 >;
И так же, как и в реальном мире, пользователь может совершать действия: выбирать что-то из корзины покупок, авторизовываться, выходить из системы, оплачивать и т.п.
Такие действия в JavaScript представлены функциями в свойствах.
Примеры методов
Для начала давайте научим нашего пользователя user здороваться:
let user = < name: "John", age: 30 >; user.sayHi = function() < alert("Привет!"); >; user.sayHi(); // Привет!
Здесь мы просто использовали Function Expression (функциональное выражение), чтобы создать функцию приветствия, и присвоили её свойству user.sayHi нашего объекта.
Затем мы можем вызвать ee как user.sayHi() . Теперь пользователь может говорить!
Функцию, которая является свойством объекта, называют методом этого объекта.
Итак, мы получили метод sayHi объекта user .
Конечно, мы могли бы использовать заранее объявленную функцию в качестве метода, вот так:
let user = < // . >; // сначала, объявляем function sayHi() < alert("Привет!"); >// затем добавляем в качестве метода user.sayHi = sayHi; user.sayHi(); // Привет!
Когда мы пишем наш код, используя объекты для представления сущностей реального мира, – это называется объектно-ориентированным программированием или сокращённо: «ООП».
ООП является большой предметной областью и интересной наукой самой по себе. Как выбрать правильные сущности? Как организовать взаимодействие между ними? Это – создание архитектуры, и на эту тему есть отличные книги, такие как «Приёмы объектно-ориентированного проектирования. Паттерны проектирования» авторов Эрих Гамма, Ричард Хелм, Ральф Джонсон, Джон Влиссидес или «Объектно-ориентированный анализ и проектирование с примерами приложений» Гради Буча, а также ещё множество других книг.
Сокращённая запись метода
Существует более короткий синтаксис для методов в литерале объекта:
// эти объекты делают одно и то же user = < sayHi: function() < alert("Привет"); >>; // сокращённая запись выглядит лучше, не так ли? user = < sayHi() < // то же самое, что и "sayHi: function()" alert("Привет"); > >;
Как было показано, мы можем пропустить ключевое слово «function» и просто написать sayHi() .
Нужно отметить, что эти две записи не полностью эквивалентны. Есть тонкие различия, связанные с наследованием объектов (что будет рассмотрено позже), но на данном этапе изучения это неважно. Почти во всех случаях сокращённый синтаксис предпочтителен.
Ключевое слово «this» в методах
Как правило, методу объекта обычно требуется доступ к информации, хранящейся в объекте, для выполнения своей работы.
Например, коду внутри user.sayHi() может потребоваться имя пользователя, которое хранится в объекте user .
Для доступа к информации внутри объекта метод может использовать ключевое слово this .
Значение this – это объект «перед точкой», который используется для вызова метода.
let user = < name: "John", age: 30, sayHi() < // "this" - это "текущий объект". alert(this.name); >>; user.sayHi(); // John
Здесь во время выполнения кода user.sayHi() значением this будет являться user (ссылка на объект user ).
Технически также возможно получить доступ к объекту без ключевого слова this , обратившись к нему через внешнюю переменную (в которой хранится ссылка на этот объект):
…Но такой код ненадёжен. Если мы решим скопировать ссылку на объект user в другую переменную, например, admin = user , и перезапишем переменную user чем-то другим, тогда будет осуществлён доступ к неправильному объекту при вызове метода из admin .
let user = < name: "John", age: 30, sayHi() < alert( user.name ); // приведёт к ошибке >>; let admin = user; user = null; // перезапишем переменную для наглядности, теперь она не хранит ссылку на объект. admin.sayHi(); // TypeError: Cannot read property 'name' of null
Если бы мы использовали this.name вместо user.name внутри alert , тогда этот код бы сработал.
«this» не является фиксированным
В JavaScript ключевое слово «this» ведёт себя иначе, чем в большинстве других языков программирования. Его можно использовать в любой функции, даже если это не метод объекта.
В следующем примере нет синтаксической ошибки:
Значение this вычисляется во время выполнения кода, в зависимости от контекста.
Например, здесь одна и та же функция назначена двум разным объектам и имеет различное значение «this» в вызовах:
let user = < name: "John" >; let admin = < name: "Admin" >; function sayHi() < alert( this.name ); >// используем одну и ту же функцию в двух объектах user.f = sayHi; admin.f = sayHi; // эти вызовы имеют разное значение this // "this" внутри функции - это объект "перед точкой" user.f(); // John (this == user) admin.f(); // Admin (this == admin) admin['f'](); // Admin (нет разницы между использованием точки или квадратных скобок для доступа к объекту)
Правило простое: если вызывается obj.f() , то во время вызова f , this – это obj . Так что, в приведённом выше примере это либо user , либо admin .
Мы даже можем вызвать функцию вообще без объекта:
function sayHi() < alert(this); >sayHi(); // undefined
В строгом режиме ( «use strict» ) в таком коде значением this будет являться undefined . Если мы попытаемся получить доступ к this.name – это вызовет ошибку.
В нестрогом режиме значением this в таком случае будет глобальный объект ( window в браузерe, мы вернёмся к этому позже в главе Глобальный объект). Это – исторически сложившееся поведение this , которое исправляется использованием строгого режима ( «use strict» ).
Обычно подобный вызов является ошибкой программирования. Если внутри функции используется this , тогда она ожидает, что будет вызвана в контексте какого-либо объекта.
Если вы до этого изучали другие языки программирования, то вы, вероятно, привыкли к идее «фиксированного this » – когда методы, определённые в объекте, всегда имеют this , ссылающееся на этот объект.
В JavaScript this является «свободным», его значение вычисляется в момент вызова метода и не зависит от того, где этот метод был объявлен, а скорее от того, какой объект вызывает метод (какой объект стоит «перед точкой»).
Эта концепция вычисления this в момент исполнения имеет как свои плюсы, так и минусы. С одной стороны, функция может быть повторно использована в качестве метода у различных объектов (что повышает гибкость). С другой стороны, большая гибкость увеличивает вероятность ошибок.
Здесь наша позиция заключается не в том, чтобы судить, является ли это архитектурное решение в языке хорошим или плохим. Скоро мы поймем, как с этим работать, как получить выгоду и избежать проблем.
У стрелочных функций нет «this»
Стрелочные функции особенные: у них нет своего «собственного» this . Если мы ссылаемся на this внутри такой функции, то оно берётся из внешней «нормальной» функции.
Например, здесь arrow() использует значение this из внешнего метода user.sayHi() :
let user = < firstName: "Ilya", sayHi() < let arrow = () =>alert(this.firstName); arrow(); > >; user.sayHi(); // Ilya
Это особенность стрелочных функций. Она полезна, когда мы на самом деле не хотим иметь отдельное this , а скорее хотим взять его из внешнего контекста. Позже в главе Повторяем стрелочные функции мы увидим больше примеров на эту тему.
Итого
- Функции, которые находятся в свойствах объекта, называются «методами».
- Методы позволяют объектам «действовать»: object.doSomething() .
- Методы могут ссылаться на объект через this .
Значение this определяется во время исполнения кода.
- При объявлении любой функции в ней можно использовать this , но этот this не имеет значения до тех пор, пока функция не будет вызвана.
- Функция может быть скопирована между объектами (из одного объекта в другой).
- Когда функция вызывается синтаксисом «метода» – object.method() , значением this во время вызова является object .
Также ещё раз заметим, что стрелочные функции являются особенными – у них нет this . Когда внутри стрелочной функции обращаются к this , то его значение берётся извне.
Задачи
Использование «this» в литерале объекта
Здесь функция makeUser возвращает объект.
Каким будет результат при обращении к свойству объекта ref ? Почему?
function makeUser() < return < name: "John", ref: this >; > let user = makeUser(); alert( user.ref.name ); // Каким будет результат?
Ответ: ошибка.
function makeUser() < return < name: "John", ref: this >; > let user = makeUser(); alert( user.ref.name ); // Error: Cannot read property 'name' of undefined
Это потому, что правила, которые определяют значение this , никак не смотрят на объявление объекта. Важен лишь момент вызова.
Здесь значение this внутри makeUser() равно undefined , потому что оно вызывается как функция, а не через «точечный» синтаксис как метод.
Значение this одно для всей функции, блоки кода и объектные литералы на него не влияют.
Таким образом, ref: this фактически принимает текущее this функции makeUser() .
Мы можем переписать функцию и вернуть то же самое this со значением undefined :
function makeUser() < return this; // на этот раз нет литерала объекта >alert( makeUser().name ); // Error: Cannot read property 'name' of undefined
Как вы можете видеть, результат alert( makeUser().name ) совпадает с результатом alert( user.ref.name ) из предыдущего примера.
Вот противоположный случай:
function makeUser() < return < name: "John", ref() < return this; >>; > let user = makeUser(); alert( user.ref().name ); // John
Теперь это работает, поскольку user.ref() – это метод. И значением this становится объект перед точкой . .
The JavaScript this Keyword
In JavaScript, the this keyword refers to an object.
Which object depends on how this is being invoked (used or called).
The this keyword refers to different objects depending on how it is used:
In an object method, this refers to the object. |
Alone, this refers to the global object. |
In a function, this refers to the global object. |
In a function, in strict mode, this is undefined . |
In an event, this refers to the element that received the event. |
Methods like call() , apply() , and bind() can refer this to any object. |
Note
this in a Method
When used in an object method, this refers to the object.
In the example on top of this page, this refers to the person object.
Because the fullName method is a method of the person object.
this Alone
When used alone, this refers to the global object.
Because this is running in the global scope.
In a browser window the global object is [object Window] :
Example
In strict mode, when used alone, this also refers to the global object:
Example
this in a Function (Default)
In a function, the global object is the default binding for this .
In a browser window the global object is [object Window] :
Example
this in a Function (Strict)
JavaScript strict mode does not allow default binding.
So, when used in a function, in strict mode, this is undefined .
Example
this in Event Handlers
In HTML event handlers, this refers to the HTML element that received the event:
Example
Object Method Binding
In these examples, this is the person object:
Example
const person = firstName : «John»,
lastName : «Doe»,
id : 5566,
myFunction : function() return this;
>
>;
Example
const person = firstName: «John»,
lastName : «Doe»,
id : 5566,
fullName : function() return this.firstName + » » + this.lastName;
>
>;
i.e. this.firstName is the firstName property of this (the person object).
Explicit Function Binding
The call() and apply() methods are predefined JavaScript methods.
They can both be used to call an object method with another object as argument.
See Also:
The example below calls person1.fullName with person2 as an argument, this refers to person2, even if fullName is a method of person1:
Example
const person1 = <
fullName: function() <
return this.firstName + » » + this.lastName;
>
>
const person2 = firstName:»John»,
lastName: «Doe»,
>
// Return «John Doe»:
person1.fullName.call(person2);
Function Borrowing
With the bind() method, an object can borrow a method from another object.
This example creates 2 objects (person and member).
The member object borrows the fullname method from the person object:
Example
const person = <
firstName:»John»,
lastName: «Doe»,
fullName: function () <
return this.firstName + » » + this.lastName;
>
>
const member = firstName:»Hege»,
lastName: «Nilsen»,
>
let fullName = person.fullName.bind(member);
This Precedence
To determine which object this refers to; use the following precedence of order.
Precedence | Object |
1 | bind() |
2 | apply() and call() |
3 | Object method |
4 | Global scope |
Is this in a function being called using bind()?
Is this in a function being called using apply()?
Is this in a function being called using call()?
Is this in an object function (method)?
Is this in a function in the global scope.