This commit is contained in:
Ilya Kantor 2014-11-16 01:40:20 +03:00
parent 962caebbb7
commit 87bf53d076
1825 changed files with 94929 additions and 0 deletions

View file

@ -0,0 +1,228 @@
# Методы объектов, 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]
## Задачи