Источник: http://learn.javascript.ru/this
Значениеthis
в JavaScript не зависит от объекта, в котором создана функция. Оно определяется во время вызова.
Любая функция может иметь в себе this
.
Совершенно неважно, объявлена она в объекте или вне него.
Значение this
называется контекстом вызова и будет определено в момент вызова функции.
Например: такая функция вполне допустима:
function sayHi() {
alert(this.firstName);
}
Эта функция ещё не знает, каким будет this
. Это выяснится при выполнении программы.
Есть несколько правил, по которым JavaScript устанавливает 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.
function func() {
"use strict";
alert(this); // выведет undefined (кроме IE<10)
}
func();
…Но по умолчанию браузеры ведут себя по-старому.
Функцию можно вызвать, явно указав значение this
.
Для этого у неё есть два метода: call
и apply
.
Синтаксис метода call
:
func.call(context, arg1, arg2,...)
При этом вызывается функция func
, первый аргумент call
становится её this
, а остальные передаются «как есть».
Вызов func.call(context, a, b...)
— то же, что обычный вызов func(a, b...)
, но с явно указанным контекстом context
.
Например, функция showName
в примере ниже вызывается через call
в контексте объекта user
:
var user = {
firstName: "Василий",
lastName: "Петров"
};
function showName() {
alert(this.firstName + ' ' + this.lastName);
}
showName.call(user) // "Василий Петров"
Можно сделать её более универсальной, добавив аргументы:
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']
.
Метод call
жёстко фиксирует количество аргументов, через запятую:
f.call(context, 1, 2, 3);
..А что, если мы захотим вызвать функцию с четырьмя аргументами? А что, если количество аргументов заранее неизвестно, и определяется во время выполнения?
Для решения этой задачи существует метод apply
.
Вызов функции при помощи func.apply
работает аналогично func.call
, но принимает массив аргументов вместо списка:
func.call(context, arg1, arg2...)
// то же что и:
func.apply(context, [arg1, arg2 ... ]);
Эти две строчки cработают одинаково:
getName.call(user, 'firstName', 'surname');
getName.apply(user, ['firstName', 'surname']);
Метод apply
гораздо мощнее, чем call
, так как можно сформировать массив аргументов динамически:
var args = [];
args.push('firstName');
args.push('surname');
func.apply(user, args); // вызовет func('firstName', 'surname') c this=user
При помощи call/apply
можно легко взять метод одного объекта, в том числе встроенного, и вызвать в контексте другого.
В JavaScript методы объекта, даже встроенные — это функции. Поэтому можно скопировать функцию, даже встроенную, из одного объекта в другой.
Это называется «одалживание метода» (на англ. method borrowing).
Используем эту технику для упрощения манипуляций с arguments
. Как мы знаем, это не массив, а обычный объект.. Но как бы хотелось вызывать на нём методы массива.
function sayHi() {
arguments.join = [].join; // одолжили метод (1)
var argStr = arguments.join(':'); // (2)
alert(argStr); // сработает и выведет 1:2:3
}
sayHi(1, 2, 3);
В строке (1) создали массив. У него есть метод [].join(..)
, но мы не вызываем его, а копируем, как и любое другое свойство в объект arguments. В строке (2) запустили его, как будто он всегда там был.
Однако, прямое копирование метода не всегда приемлемо.
Для безопасного вызова используем apply/call:
function sayHi() {
var join = [].join; // ссылка на функцию теперь в переменной
// вызовем join с this=arguments,
// этот вызов эквивалентен arguments.join(':') из примера выше
var argStr = join.call(arguments, ':');
alert(argStr); // сработает и выведет 1:2:3
}
sayHi(1, 2, 3);
В JavaScript есть очень простой способ сделать из arguments настоящий массив.
Вызовем метод массива arr.slice(start, end)
в контексте arguments
:
function sayHi() {
// вызов arr.slice() скопирует все элементы из this в новый массив
var args = [].slice.call(arguments);
alert(args.join(':')); // args -- массив аргументов
}
sayHi(1,2);
При помощи apply
мы можем сделать универсальную «переадресацию» вызова из одной функции в другую.
Например, функция f
вызывает g
в том же контексте, с теми же аргументами:
function f(a, b) {
g.apply(this, arguments);
}
Плюс этого подхода — в том, что он полностью универсален:
f
добавятся новые аргументы.f
является методом объекта, то текущий контекст также будет передан. Если не является — то this
здесь вроде как не при чём, но и вреда от него не будет.Значение this
устанавливается в зависимости от того, как вызвана функция:
При вызове функции как метода
obj.func(...) // this = obj
obj["func"](...)
При обычном вызове
func(...) // this = window
В new
new func() // this = {} (новый объект)
Явное указание
func.apply(ctx, args) // this = ctx (новый объект)
func.call(ctx, arg1, arg2, ...)