this

Поведение ключевого слова this в JavaScript несколько отличается по сравнению с остальными языками. Имеются также различия при использовании this в строгом и нестрогом режиме.

В большинстве случаев значение this определяется тем, каким образом вызвана функция. Значение this не может быть установлено путём присваивания во время исполнения кода и может иметь разное значение при каждом вызове функции. В ES5 представлен метод bind(), который используется для привязки значения ключевого слова this независимо от того, как вызвана функция. Также в ES2015 представлены стрелочные функции, которые не создают собственные привязки к this (они сохраняют значение this лексического окружения, в котором были созданы).

Интерактивный пример

Синтаксис

this

Значение

Свойство контекста выполнения кода (global, function или eval), которое в нестрогом режиме всегда является ссылкой на объект, а в строгом режиме может иметь любое значение.

Global контекст

В глобальном контексте выполнения (за пределами каких-либо функций) this ссылается на глобальный объект вне зависимости от режима (строгий или нестрогий).

js
// В браузерах, объект window также является объектом global:
console.log(this === window); // true

a = 37;
console.log(window.a); // 37

this.b = "MDN";
console.log(window.b); // "MDN"
console.log(b); // "MDN"

Примечание: вы всегда можете легко получить объект global, используя глобальное свойство globalThis, независимо от текущего контекста, в котором выполняется ваш код.

Function контекст

В пределах функции значение this зависит от того, каким образом вызвана функция.

Простой вызов

Поскольку следующий код не в строгом режиме, и значение this не устанавливается вызовом, по умолчанию будет использоваться объект global, которым в браузере является window.

js
function f1() {
  return this;
}

// В браузере:
f1() === window; // window - глобальный объект в браузере

// В Node:
f1() === global; // global - глобальный объект в Node

В строгом режиме, если значение this не установлено в контексте выполнения, оно остаётся undefined, как показано в следующем примере:

js
function f2() {
  "use strict"; // см. strict mode
  return this;
}

f2() === undefined; // true

Примечание: Во втором примере this должно иметь значение undefined, потому что функция f2 была вызвана напрямую, а не как метод или свойство объекта (например, window.f2()). Реализация этой особенности не поддерживалась в некоторых браузерах, когда они впервые начали поддерживать строгий режим. В результате они некорректно возвращали объект window.

Для того, чтобы при вызове функции установить this в определённое значение, используйте call() или apply(), как в следующих примерах.

Пример 1

js
// В качестве первого аргумента методов call или apply может быть передан объект,
// на который будет указывать this.
var obj = { a: "Custom" };

// Это свойство принадлежит глобальному объекту
var a = "Global";

function whatsThis() {
  return this.a; //значение this зависит от контекста вызова функции
}

whatsThis(); // 'Global'
whatsThis.call(obj); // 'Custom'
whatsThis.apply(obj); // 'Custom'

Пример 2

js
function add(c, d) {
  return this.a + this.b + c + d;
}

var o = { a: 1, b: 3 };

// Первый параметр - это объект для использования в качестве
// 'this', последующие параметры передаются как
// аргументы функции call
add.call(o, 5, 7); // 16

// Первый параметр - это объект для использования в качестве
// 'this', второй - массив, чьи члены используются
// в качестве аргументов функции call
add.apply(o, [10, 20]); // 34

Обратите внимание, что в нестрогом режиме, если значение, переданное в call или apply как this, не является объектом, будет сделана попытка преобразовать его в объект с помощью внутренней операции ToObject. Таким образом, если переданное значение является примитивом, таким как 7 или 'foo', оно будет преобразовано в Object с использованием связанного конструктора, так что примитивное число 7 будет преобразовано в объект так, как будто с помощью new Number(7), а строка 'foo' - как будто с помощью new String('foo'), например

js
function bar() {
  console.log(Object.prototype.toString.call(this));
}

bar.call(7); // [object Number]
bar.call("foo"); // [object String]

Метод bind()

Вызов f.bind(someObject) создаёт новую функцию с таким же телом и окружением, что и у f, но значение this указывает на первый аргумент bind, независимо от того, как вызывается функция.

js
function f() {
  return this.a;
}

const g = f.bind({ a: "qwerty" });
console.log(g()); // qwerty

const h = g.bind({ a: "yoo" }); // bind сработает только один раз!
console.log(h()); // qwerty

const o = { a: 37, f, g, h };
console.log(o.a, o.f(), o.g(), o.h()); // 37 37 qwerty qwerty

this в стрелочных функциях

Стрелочные функции создают замыкания для значения this из окружающего контекста выполнения. В следующем примере мы создаём объект obj с методом getThisGetter, который возвращает функцию, которая возвращает значение this. Возвращаемая функция является стрелочной, поэтому её this связано с this окружающей функции. Значение this внутри getThisGetter может быть установлено при вызове, который, в свою очередь, устанавливает возвращаемое значение возвращаемой функции. Мы будем считать, что getThisGetter является нестрогой функцией, то есть она находится внутри нестрогого скрипта и не вложена в класс или строгую функцию.

js
const obj = {
  getThisGetter() {
    const getter = () => this;
    return getter;
  },
};

Если вызвать getThisGetter как метод объекта obj, то это свяжет this с obj внутри его тела. Возвращаемая функция присвоена переменной fn. Теперь при вызове fn возвращаемое значение this по-прежнему задаётся вызовом getThisGetter, то есть obj. Если бы возвращаемая функция не была стрелочной, то при таких вызовах значение this было бы globalThis, поскольку getThisGetter не является строгой.

js
const fn = obj.getThisGetter();
console.log(fn() === obj); // true

Но будьте осторожны при отвязывании метода obj без его вызова, потому что getThisGetter всё ещё метод, который имеет изменяющееся значение this. Вызов fn2()() в следующем примере возвращает globalThis, потому что он следует за this из fn2(), который является globalThis, поскольку вызывается без привязки к какому-либо объекту.

js
const fn2 = obj.getThisGetter;
console.log(fn2()() === globalThis); // true в нестрогом режиме

Такое поведение очень полезно при определении обратных вызовов. Обычно каждое функциональное выражение создаёт свою собственную привязку this, которая перекрывает значение this окружающей области видимости. Если вам не важно значение this, вы можете определять функции как стрелочные и создавать привязки this только там, где это необходимо (например, в методах класса). Смотрите пример с setTimeout().

В методе объекта

Когда функция вызывается как метод объекта, используемое в этой функции ключевое слово this принимает значение объекта, по отношению к которому вызван метод.

В следующем примере, когда вызвано свойство o.f() , внутри функции this привязано к объекту o.

js
var o = {
  prop: 37,
  f: function () {
    return this.prop;
  },
};

console.log(o.f()); // logs 37

Необходимо отметить, что на поведение this совсем не влияет то, как или где была определена функция. В предыдущем примере мы определили функцию внутри свойства f во время определения объекта o. Однако, мы могли бы также просто определить сначала функцию, а затем закрепить её за свойством o.f. В этом случае поведение this не изменится:

js
var o = { prop: 37 };

function independent() {
  return this.prop;
}

o.f = independent;

console.log(o.f()); // logs 37

Эти примеры показывают, что имеет значение только то, что функция была вызвана из свойства f объекта o.

Аналогично, привязывание this обуславливается наличием ближайшей ссылки на объект или свойство. В следующем примере, когда мы вызываем функцию, мы обращаемся к ней как к методу g объекта o.b. На этот раз во время выполнения, this, что находится внутри функции, будет ссылаться на o.b. Тот факт, что объект является членом объекта o, не имеет значения; важна только ближайшая ссылка.

js
o.b = { g: independent, prop: 42 };
console.log(o.b.g()); // logs 42

this в цепочке object's prototype

Это же представление справедливо и для методов, определённых где-либо в цепочке object's prototype. Если метод находится в цепочке прототипов, то this ссылается на объект, на котором был вызван метод, т.е. так, словно метод является методом самого объекта, а не прототипа.

js
var o = {
  f: function () {
    return this.a + this.b;
  },
};
var p = Object.create(o);
p.a = 1;
p.b = 4;

console.log(p.f()); // 5

В этом примере объект, который присвоен переменной p, не имеет собственного свойства f, а наследует это свойство от своего прототипа. Однако, совершенно неважно, что поиск свойства f в конце концов обнаружит его на объекте o. Поскольку поиск начался с p.f, то и свойство this внутри функции f будет ссылаться на объект p. Таким образом, если f вызывается как метод p, то и this относится к p. Это полезная особенность прототипного наследования JS.

this с геттерами/сеттерами

Все те же утверждения справедливы, если функция вызывается из геттера или сеттера. Для функции, которая используется как геттер или сеттер this привязан к объекту, свойство которого необходимо извлечь через геттер/сеттер.

js
function modulus() {
  return Math.sqrt(this.re * this.re + this.im * this.im);
}

var o = {
  re: 1,
  im: -1,
  get phase() {
    return Math.atan2(this.im, this.re);
  },
};

Object.defineProperty(o, "modulus", {
  get: modulus,
  enumerable: true,
  configurable: true,
});

console.log(o.phase, o.modulus); // logs -0.78 1.4142

В конструкторе

Когда функция используется как конструктор (с ключевым словом new ), this связано с создаваемым новым объектом.

Примечание: по умолчанию конструктор возвращает объект, на который ссылается this, но он может вернуть и другой объект (если возвращаемое значение не является объектом, тогда будет возвращён объект с this).

js
/*
 * Конструктор работает таким образом:
 *
 * function MyConstructor(){
 *   // фактический код, составляющий тело функции.
 *   // создание свойств с |this| по
 *   // желанию, определяя их значения; например,
 *   this.fum = "nom";
 *   // и т.д.
 *
 *   // Если функция возвращает выражение,
 *   // возвращающее объект, этот объект будет
 *   // результатом выражения |new|. В обратном случае,
 *   // результат выражения - объект,
 *   // в данный момент привязанный к |this|
 *   // (т.е. наиболее часто встречающийся случай).
 * }
 */

function C() {
  this.a = 37;
}

var o = new C();
console.log(o.a); // logs 37

function C2() {
  this.a = 37;
  return { a: 38 };
}

o = new C2();
console.log(o.a); // logs 38

В последнем примере (C2), из-за того, что конструктор вернул объект, новый объект, к которому было привязано this, был просто отброшен. (Это фактически делает выражение "this.a = 37;" "мёртвым" кодом. Он не является буквально нерабочим, так как он выполняется, но он может быть изъят без каких-либо внешних эффектов.)

call и apply

Когда в теле функции используется ключевое слово this, его значение может быть привязано к конкретному объекту в вызове при помощи методов call или apply, которые наследуются всеми функциями от Function.prototype.

js
function add(c, d) {
  return this.a + this.b + c + d;
}

var o = { a: 1, b: 3 };

// Первый параметр - это объект, который следует использовать как
// 'this', последующие параметры передаются
// как аргументы при вызове функции
add.call(o, 5, 7); // 1 + 3 + 5 + 7 = 16

// Первый параметр - объект, который следует использовать как
// 'this', второй параметр - массив,
// элементы которого используются как аргументы при вызове функции
add.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34

Необходимо отметить, что если методам call и apply передаётся значение с this, которое не является при этом объектом, будет предпринята попытка конвертировать значение в объект, используя внутреннюю операцию ToObject. Если переданное значение является примитивным типом, например 7 или 'foo', оно будет преобразовано в объект с использованием родственного конструктора, так примитив 7 преобразовывается в объект через new Number(7), а строка 'foo' в объект через new String('foo'), и т.д.

js
function bar() {
  console.log(Object.prototype.toString.call(this));
}

bar.call(7); // [object Number]

Как обработчик событий DOM

Когда функция используется как обработчик событий, this присваивается элементу с которого начинается событие (некоторые браузеры не следуют этому соглашению для обработчиков, добавленных динамически с помощью всех методов, кроме addEventListener).

js
// Когда вызывается как обработчик, связанный элемент становится синим
function bluify(e) {
  // Всегда true
  console.log(this === e.currentTarget);
  // true, когда currentTarget и target один объект
  console.log(this === e.target);
  this.style.backgroundColor = "#A5D9F3";
}

// Получить список каждого элемента в документе
var elements = document.getElementsByTagName("*");

// Добавить bluify как обработчика кликов, чтобы при
// нажатии на элемент он становился синим
for (var i = 0; i < elements.length; i++) {
  elements[i].addEventListener("click", bluify, false);
}

В инлайновом обработчике событий

Когда код вызван из инлайнового обработчика, this указывает на DOM-элемент, в котором расположен код события:

js
<button onclick="alert(this.tagName.toLowerCase());">Показать this</button>

Код выше выведет 'button'. Следует отметить, что this будет указывать на DOM-элемент только во внешних (не вложенных) функциях:

js
<button onclick="alert((function() {return this;} ()));">
  Показать вложенный this
</button>

В этом случае this вложенной функции не будет установлен, так что будет возвращён global/window объект.

Спецификации

Specification
ECMAScript Language Specification
# sec-this-keyword

Совместимость с браузерами

BCD tables only load in the browser

Смотрите также