- Вызов метода
- Вызов в режиме обычной функции
- Вызов функции с
new - Передача
thisпри помощиcall/apply - Итого
Значение this в JavaScript не зависит от объекта, в котором создана функция. Оно определяется во время вызова.
Вспомним «стандартное» поведение this. Обычно, когда функция вызывается в контексте объекта, то этот объект доступен как this:
var user = {
firstName: "Вася",
sayHi: function() {
alert( this.firstName ); // при вызове user.sayHi() будет this = user
}
}
Но, в отличие от многих других языков, this в JavaScript может использовать любая функция. Не важно, объявлена она в объекте или вне него.
Значение this называется контекстом вызова и будет определено в момент вызова функции.
Например: такая функция вполне допустима:
function func() {
alert( *!*this.firstName*/!* );
}
Эта функция еще не знает, каким будет this. Вполне возможно, что этого пока не знаем и мы. Это выяснится при выполнении программы.
Вызов метода
Самый распространенный случай - когда функция изначально объявлена в объекте или в конечном счете присваивается ему, как в примере ниже:
var user = {
firstName: "Вася"
};
function func() {
alert( this.firstName );
}
*!*
user.sayHi = func;
*/!*
user.sayHi(); // this = user
При вызове функции как метода объекта, через точку или квадратные скобки — функция получает в this этот объект. В данном случае user.sayHi() присвоит this = user.
Если одну и ту же функцию запускать в контексте разных объектов, она будет получать разный this:
var user = { firstName: "Вася" };
var admin = { firstName: "Админ" };
function func() {
alert( this.firstName );
}
*!*
user.a = func; // присвоим одну функцию в свойства
admin.b = func; // двух разных объектов user и admin
user.a(); // Вася
admin.b(); // Админ
*/!*
Значение this не зависит от того, как функция была создана, оно определяется исключительно в момент вызова.
Вызов в режиме обычной функции
Если функция использует this - это подразумевает работу с объектом. Но и прямой вызов func() технически возможен.
Как правило, такая ситуация возникает при ошибке в разработке.
При этом this получает значение window, глобального объекта.
function func() {
alert(this); // выведет [object Window] или [object global]
}
func();
В современном стандарте языка, это поведение изменено, вместо глобального объекта this будет undefined. Но по умолчанию браузеры ведут себя по-старому.
Как вы думаете, эти вызовы obj.go работают одинаково?
obj = {
go: function() { alert(this) }
};
obj.go();
(obj.go)();
(a = obj.go)();
(0 || obj.go)();
.. При запуске легко видеть - не одинаково. Почему?
Чтобы быть переданным в this, значение слева от точки должно быть объектом, но не выражением. Допустимы варианты obj.a или obj['a'], но ничего более сложного.
Скобки не влияют на это, так как операция получения свойства obj.go возвращает значение специального внутреннего типа Reference Type, который содержит информацию о go и obj. При любой операции Reference Type теряется и остаётся информация только о go.
Поэтому любое другое выражение, такое как (a = obj.method)() или (a.method || b.method)() интерпретируется как два шага: a = obj.method, а затем вызов без контекста a().
Каков будет результат этого кода?
obj = {
go: function() { alert(this) }
}
(obj.go || 0)()
P.S. Решение неожиданное, в задаче - «подводный камень»
Ошибка! Попробуйте:
obj = {
go: function() { alert(this) }
}
(obj.go || 0)() // error!
Причем сообщение об ошибке - очень странное. В большинстве браузеров это obj is undefined.
Дело, как ни странно, ни в самом объявлении obj, а в том, что после него пропущена точка с запятой.
JavaScript игнорирует перевод строки перед скобкой (obj.go || ..) и читает этот код как:
obj = { go:... }(obj.go || 0)()
Интерпретатор пытается вызывать объект { go: ... } как функцию, что и дает ошибку.
А что будет, если добавить точку с запятой?
obj = {
go: function() { alert(this); }
}*!*;*/!*
(obj.go || 0)();
Результат — window, поскольку вызов obj.go || 0 аналогичен коду:
obj = {
go: function() { alert(this); }
};
var f = obj.go || 0; // эти две строки - аналог (obj.go || 0)();
f(); // window
Каким будет результат? Почему?
arr = ["a", "b"];
arr.push( function() { alert(this); } )
arr[2](); // ?
Вызов arr[2]() — это обращение к методу объекта obj[method](), в роли obj выступает arr, а в роли метода: 2.
Поэтому, как это бывает при вызове функции как метода, функция arr[2] получит this = arr и выведет массив:
arr = ["a", "b"];
arr.push( function() { alert(this); } )
arr[2](); // "a","b",function
Вызов функции с new
При вызове функции с new, значением this является новосоздаваемый объект. Мы уже обсуждали это в разделе о создании объектов.
Передача this при помощи call/apply
Функцию можно вызвать, явно указав значение this. Для этого у неё есть два метода: call и apply.
call
Синтаксис метода call:
func.call(_this, arg1, arg2,...)
Первый аргумент call становится this, а остальные передаются функции.
Например, функция send в примере ниже вызывается через call в контексте объекта user:
var user = { firstName: "Вася" };
function send(who, message) {
alert( 'от ' + this.firstName + ': ' + who + ', ' + message );
}
*!*
send.call(user, 'Петя', 'Привет') // "от Вася: Петя, Привет"
*/!*
Вызов func.call(_this, arg1, arg2...) — то же, что обычный вызов func(arg1, arg2...), но с дополнительно указанным контекстом _this.
Еще пример:
var user = {
firstName: "Василий",
surname: "Петров"
};
function getName(a, b) {
alert( this[a] + ' ' + this[b] )
}
*!*
getName.call(user, 'firstName', 'surname') // "Василий Петров"
*/!*
Здесь функция getName вызвана с контекстом this = user и выводит user['firstName'] и user['surname'].
apply
Вызов функции при помощи func.apply работает аналогично func.call, но принимает массив аргументов вместо списка:
func.call(_this, arg1, arg2...) // то же что и: func.apply(_this, [arg1, arg2 ... ]);
Эти две строчки cработают одинаково:
getName.call(user, 'firstName', 'surname'); getName.apply(user, ['firstName', 'surname']);
Использование массива вместо перечисления аргументов дает больше возможностей. Например, можно сформировать массив аргументов динамически:
var args = [];
args.push('firstName');
args.push('surname');
func.apply(user, args)
call/apply(null/undefined)
При указании первого аргумента null или undefined в call/apply, функция получает this = window:
function f() { alert(this) }
f.call(null); // window
Это поведение исправлено в современном стандарте (15.3).
Если функция работает в строком режиме, то this передаётся «как есть»:
function f() {
"use strict";
alert(this); // null, "как есть"
}
f.call(null);
Итого
Значение this устанавливается в зависимости от того, как вызвана функция:
- При вызове функции как метода
-
obj.func(...) // this = obj obj["func"](...)
- При обычном вызове
-
func(...) // this = window
- В
new -
new func() // this = {} (новый объект) call/apply-
func.call(context, arg1, arg2, ...) // this = context func.apply(context, [args])
Комментарии
- Приветствуются комментарии, содержащие дополнения и вопросы по статье, и ответы на них.
- Если ваш комментарий касается задачи -- откройте её в отдельном окне и напишите там.
- Комментарии без смысла, с рекламой или не о статье вообще - удаляются.