en.javascript.info/1-js/6-objects-more/1-object-methods/article.md
Ilya Kantor 87bf53d076 update
2014-11-16 01:40:20 +03:00

228 lines
7.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Методы объектов, this
До этого мы говорили об объекте лишь как о хранилище значений. Теперь пойдём дальше и поговорим об объектах как о сущностях со своими функциями ("методами").
[cut]
## Методы у объектов
При объявлении объекта можно указать свойство-функцию, например:
```js
//+ run
var user = {
name: 'Василий',
*!*
// метод
*/!*
sayHi: function() {
alert('Привет!');
}
};
*!*
// Вызов
user.sayHi();
*/!*
```
Свойства-функции называют "методами" объектов. Их можно добавлять и удалять в любой момент, в том числе и явным присваиванием:
```js
//+ run
var user = {
name: 'Василий'
};
*!*
user.sayHi = function() { // присвоили метод после создания объекта
alert('Привет!');
};
*/!*
// Вызов метода:
*!*user.sayHi();*/!*
```
## Доступ к объекту через this
Для полноценной работы метод должен иметь доступ к данным объекта. В частности, вызов `user.sayHi()` может захотеть вывести имя пользователя.
**Для доступа к текущему объекту из метода используется ключевое слово `this`**.
Значением `this` является объект перед "точкой", в контексте которого вызван метод, например:
```js
//+ run
var user = {
name: 'Василий',
sayHi: function() {
alert( *!*this.name*/!* );
}
};
user.sayHi(); // sayHi в контексте user
```
Здесь при выполнении функции `user.sayHi()` в `this` будет храниться ссылка на текущий объект `user`.
Вместо `this` внутри `sayHi` можно было бы обратиться к объекту, используя переменную `user`:
```js
...
sayHi: function() {
alert( *!*user.name*/!* );
}
...
```
...Однако, такое решение нестабильно. Если мы решим скопировать объект в другую переменную, например `admin = user`, а в переменную `user` записать что-то другое -- обращение будет совсем не по адресу:
```js
//+ run
var user = {
name: 'Василий',
sayHi: function() {
alert( *!*user.name*/!* ); // приведёт к ошибке
}
};
var admin = user;
user = null;
admin.sayHi(); // упс! внутри sayHi обращение по старому имени, ошибка!
```
**Использование `this` гарантирует, что функция работает именно с тем объектом, в контексте которого вызвана!**
Через `this` метод может обратиться к любому свойству объекта, а, при желании, и передать объект куда-либо:
```js
//+ run
var user = {
name: 'Василий',
*!*
sayHi: function() {
showName(this); // передать текущий объект в showName
}
*/!*
};
function showName(obj) {
alert( obj.name );
}
user.sayHi(); // Василий
```
## Подробнее про this
Любая функция может иметь в себе `this`. Совершенно неважно, объявлена она в объекте или вне него.
Значение `this` называется *контекстом вызова* и будет определено в момент вызова функции.
Например, такая функция, объявленная без объекта, вполне допустима:
```js
function sayHi() {
alert( *!*this.firstName*/!* );
}
```
Эта функция ещё не знает, каким будет `this`. Это выяснится при выполнении программы.
**Если одну и ту же функцию запускать в контексте разных объектов, она будет получать разный `this`:**
```js
//+ run
var user = { firstName: "Вася" };
var admin = { firstName: "Админ" };
function func() {
alert( this.firstName );
}
user.f = func;
admin.g = func;
*!*
// this равен объекту перед точкой:
user.f(); // Вася
admin.g(); // Админ
admin['g'](); // Админ (не важно, доступ к объекту через точку или квадратные скобки)
*/!*
```
**Значение `this` не зависит от того, как функция была создана, оно определяется исключительно в момент вызова.**
## Значение this при вызове без контекста
Если функция использует `this` -- это подразумевает работу с объектом. Но и прямой вызов `func()` технически возможен.
Как правило, такая ситуация возникает при ошибке в разработке.
При этом `this` получает значение `window`, глобального объекта:
```js
//+ run
function func() {
alert(this); // выведет [object Window] или [object global]
}
func();
```
В современном стандарте языка это поведение изменено, вместо глобального объекта `this` будет `undefined`.
```js
//+ run
function func() {
"use strict";
alert(this); // выведет undefined (кроме IE<10)
}
func();
```
Это стоит иметь в виду для общего развития, но обычно если в функции используется `this`, то она, всё же, проектируется для вызова в контексте объекта.
[warn header="`this` теряется при операциях с методом"]
Ещё раз обратим внимание: контекст `this` никак не привязан к функции, даже если она создана в объявлении объекта.
Чтобы `this` передался правильно, нужно вызвать функцию именно через точку (или квадратные скобки). Любой более хитрый вызов приведёт к потере контекста, например:
```js
//+ run
var user = {
name: "Вася",
hi: function() { alert(this.name); },
bye: function() { alert("Пока"); }
};
user.hi(); // Вася (простой вызов работает)
*!*
// а теперь вызовем user.hi или user.bye в зависимости от имени
(user.name == "Вася" ? user.hi : user.bye)(); // undefined
*/!*
```
В последней строке примера метод получен в результате выполнения тернарного оператора и тут же вызван. При этом `this` теряется.
Иначе говоря, такой вызов эквивалентен двум строкам:
```js
var method = (user.name == "Вася" ? user.hi : user.bye);
method(); // без this
```
[/warn]
## Задачи