В процессе перевода.

Сводка

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

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

Синтаксис

this

Глобальный контекст

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

console.log(this.document === document); // true

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

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

В контексте функции

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

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

В этом случае значение this не устанавливается вызовом. Так как этот код написан не в строгом режиме, значением this всегда должен быть объект, по умолчанию - глобальный объект.

function f1(){
  return this;
}
// В браузере
f1() === window; // window глобальный объект в браузере

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

function f2(){
  "use strict"; // see strict mode
  return this;
}

f2() === undefined;

Итак, в строгом режиме если this не определено оно остается не определено.

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

Для того что бы передать значение this от одного контекста другому необходимо использовать call или apply

Стрелочные функции

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

var globalObject = this;
var foo = (() => this);
console.log(foo() === globalObject); // true

Не важно, как функция foo() будет вызвана, ее this будет указывать на глобальный объект. this будет сохранять свое значение, даже если функция foo() будет вызвана как метод обьекта (что в обычных функциях связывает this с объектом вызова) или с использованием методов call, apply или bind:

// Вызов функции как метода объекта
var obj = {foo: foo};
console.log(obj.foo() === globalObject); // true

// Попытка установить this с помощью call
console.log(foo.call(obj) === globalObject); // true

// Попытка установить this с помощью bind
foo = foo.bind(obj);
console.log(foo() === globalObject); // true

Независимо от этого, this функции foo() имеет тоже значение, что и при создании функции (глобальный объект в примере выше). То же самое касается стрелочных функций созданных внутри других функций: их this будет привязан к окружению.

// Создаем объект obj с методом bar который возвращает функцию
// которая возвращает свой this. Возвращаемая функция создана
// как стрелочная функция, таким образом ее this замкнут 
// на this функции в которой она создана. 
var obj = { bar : function() {
                    var x = (() => this);
                    return x;
                  }
          };

// Вызываем bar как метод объекта obj, устанавливая его this на obj
// Присваиваем ссылку возвращаемой функции fn
var fn = obj.bar();

// Вызываем fn без установки this, что в обычных функциях указывало бы
// на глобальный объект или undefined в строгом режиме.
console.log(fn() === obj); // true

В примере выше, функция (назовем ее анонимной функцией A) присвоенная  obj.bar возвращает другую функцию (назовем ее анонимной функцией B) которая создана как стрелочная функция. В результате вызова функции A, this функции B замкнут на this, принадлежащий obj.bar (функции A). this функции B всегда будет иметь то значение которое он получил при создании. В примере выше, this функции B указывает на this функции A,который указывает на obj, таким образом this будет указывать на obj даже когда будет вызван методом который в нормальных условиях устанавливал значение this равным undefined или глобальному обьекту ( или любым другим методом, как в предыдущих примерах).

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

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

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

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

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

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

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 не имеет значения; важна только ближайшая ссылка.

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

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

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

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 привязан к объекту, свойство которого необходимо извлечь через геттер/сеттер.

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).

/*
 * Контруктор работает таким образом:
 *
 * 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.

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'), и т.д.

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

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

Метод bind 

ECMAScript 5 ввел Function.prototype.bind. Вызов f.bind(someObject) создает новую функцию с тем же телом и областью видимости что и  f, но там, где находится this в исходной функции, в новой функции существует постоянная привязка к первому аргументу метода bind, несмотря на то, как используется данная функция.

function f(){
  return this.a;
}

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

var o = {a:37, f:f, g:g};
console.log(o.f(), o.g()); // 37, azerty

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

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

// When called as a listener, turns the related element blue
function bluify(e){
  // Always true
  console.log(this === e.currentTarget); 
  // true when currentTarget and target are the same object
  console.log(this === e.target);
  this.style.backgroundColor = '#A5D9F3';
}

// Get a list of every element in the document
var elements = document.getElementsByTagName('*');

// Add bluify as a click listener so when the
// element is clicked on, it turns blue
for(var i=0 ; i<elements.length ; i++){
  elements[i].addEventListener('click', bluify, false);
}

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

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

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

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

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

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

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

Specification Status Comment
ECMAScript 1st Edition. Standard Initial definition. Implemented in JavaScript 1.0
ECMAScript 5.1 (ECMA-262)
Определение 'The this keyword' в этой спецификации.
Стандарт  
ECMAScript 2015 (6th Edition, ECMA-262)
Определение 'The this keyword' в этой спецификации.
Стандарт  

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

Feature Chrome Firefox (Gecko) Internet Explorer Opera Safari
Базовая поддержка (Да) (Да) (Да) (Да) (Да)
Feature Android Chrome for Android Firefox Mobile (Gecko) IE Mobile Opera Mobile Safari Mobile
Базовая поддержка (Да) (Да) (Да) (Да) (Да) (Да)

Смотри также

Метки документа и участники

 Обновлялась последний раз: aprostya,