this

Ключове слово функції this поводиться дещо інакше у JavaScript у порівнянні з іншими мовами. Воно також має певні відмінності між строгим та нестрогим режимами.

У більшості випадків, значення this визначається тим, як викликається функція (прив'язка під час виконання). Воно не може бути переприсвоєне під час виконання, і може змінюватись кожен раз, коли викликається функція. ES5 запровадив метод bind() для присвоєння значення this у функції незалежно від того, як вона викликається, а ES2015 запровадив стрілкові функції, які не мають власної прив'язки this (вони зберігають значення this оточуючого лексичного контексту).

Синтаксис

this

Значення

Властивість контексту виконання (глобального, функції чи eval), яка, у нестрогому режимі завжди посилається на об'єкт, а у строгому режимі може бути будь-яким значенням.

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

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

// У веб-переглядачах об'єкт window також є глобальним об'єктом:
console.log(this === window); // true

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

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

Заувага: Ви завжди легко можете отримати глобальний об'єкт за допомогою глобальної властивості globalThis, незалежно від поточного контексту, у якому виконується ваш код.

Контекст функції

Всередині функції значення this залежить від того, як викликається функція.

Простий виклик

Оскільки наступний код не є кодом у строгому режимі, і тому, що значення this не було присвоєне викликом, this за замовчуванням дорівнюватиме глобальному об'єкту, яким у переглядачі є window

function f1() {
  return this;
}

// У переглядачі:
f1() === window; // true 

// У Node:
f1() === global; // true

Однак, у строгому режимі значення this не присвоюється при вході у контекст виконання, воно залишається undefined, як показано у наступному прикладі:

function f2() {
  'use strict'; // дивіться строгий режим
  return this;
}

f2() === undefined; // true
У другому прикладі this має дорівнювати undefined, тому що функція f2 була викликана прямо, а не як метод чи властивість об'єкта (наприклад, window.f2()). Ця функціональність не була реалізована у деяких переглядачах, коли вони вперше почали підтримувати строгий режим. Як результат, вони неправильно повертали об'єкт window.

Для присвоєння this певного значення під час виклику функції використовуйте call() або apply(), як у наступних прикладах.

Приклад 1

// Об'єкт може бути переданий першим аргументом у call чи apply, і буде прив'язаний до this.
var obj = {a: 'Користувацький'};

// Ця властивість створена у глобальному об'єкті
var a = 'Глобальний';

function whatsThis() {
  return this.a;  // Значення this залежить від того, як викликається функція
}

whatsThis();          // 'Глобальний'
whatsThis.call(obj);  // 'Користувацький'
whatsThis.apply(obj); // 'Користувацький'

Приклад 2

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

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

// Перший параметр - це об'єкт для використання в якості
// 'this', наступні параметри передаються як аргументи
// для виклику фукнції
add.call(o, 5, 7); // 16

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

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

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

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

Метод bind

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

function f() {
  return this.a;
}

var g = f.bind({a: 'привіт'});
console.log(g()); // привіт

var h = g.bind({a: 'йо'}); // bind працює лише один раз!
console.log(h()); // привіт

var o = {a: 37, f: f, g: g, h: h};
console.log(o.a, o.f(), o.g(), o.h()); // 37,37, привіт, привіт

Стрілкові функції

У стрілкових функціях this зберігає значення this оточуючого лексичного контексту. У глобальному коді йому буде присвоєно глобальний об'єкт:

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

Заувага: якщо this передається як аргумент до call, bind чи apply під час виклику стрілкової функції, він буде проігнорований. Ви все одно можете подавати аргументи до call, але перший аргумент (thisArg) має бути null.

// Виклик в якості методу об'єкта
var obj = {func: foo};
console.log(obj.func() === 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 оточуючої функції. Можна присвоювати значення bar
// під час виклику, що в свою чергу присвоїть значення 
// поверненої функції.
var obj = {
  bar: function() {
    var x = (() => this);
    return x;
  }
};

// Викликаємо bar як метод obj, встановлюючи obj значенням його this
// Присвоюємо посилання на повернену функцію у fn
var fn = obj.bar();

// Виклик fn без присвоєння this мав би присвоїти
// за замовчуванням глобальний об'єкт або undefined у строгому режимі
console.log(fn() === obj); // true

// Але будьте обережні, якщо посилаєтесь на метод obj, не викликаючи його
var fn2 = obj.bar;
// Виклик this стрілкової функції зсередини методу bar
// тепер поверне window, бо він бере this з fn2.
console.log(fn2()() == window); // true

У наведеному коді функція (назвемо її анонімною функцією А), присвоєна obj.bar, повертає іншу функцію (назвемо її анонімною функцією Б), яка створюється як стрілкова функція. В результаті this функції Б незмінно дорівнює this з obj.bar (функції A) під час виклику. Коли викликається повернена функція (функція Б), її this завжди дорівнюватиме початково присвоєному значенню. У наведеному прикладі this функції Б присвоюється значення this функції А, яке дорівнює obj, отже, воно залишається рівним obj, навіть коли викликається таким способом, який мав би присвоїти this значення undefined або глобальний об'єкт (чи будь-яким іншим методом, як у попередньому прикладі у глобальному контексті виконання).

Як метод об'єкта

Коли функція викликається як метод об'єкта, її this присвоюється об'єкт, на якому викликається метод.

У наступному прикладі, коли викликається o.f(), всередині функції this прив'язується до об'єкта o.

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

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

Зауважте, що на цю поведінку зовсім не впливає те, як і де була визначена функція. У попередньому прикладі ми визначили функцію f вбудованою, як член об'єкта, під час визначення o. Однак, ми так само легко могли спочатку визначити функцію, а потім додати її до o.f. Це призводить до такої самої поведінки:

var o = {prop: 37};

function independent() {
  return this.prop;
}

o.f = independent;

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

Це демонструє, що значення має тільки те, що функція була викликана з f, члена об'єкта o.

Схожим чином, на прив'язку this впливає тільки найближче посилання на об'єкт. У наступному прикладі, коли ми викликаємо функцію, ми викликаємо її як метод g об'єкта o.b. Цього разу під час виконання this всередині функції посилатиметься на o.b. Той факт, що сам об'єкт є членом об'єкта o, не має наслідків; все, що має значення, це найближче посилання.

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

this у ланцюжку прототипів об'єкта

Такий самий принцип діє для методів, визначених десь у ланцюжку прототипів об'єкта. Якщо метод присутній у ланцюжку прототипів об'єкта, 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 всередині функції приймає значення об'єкта, на який посилається p. Тобто, оскільки f викликається як метод p, його this посилається на p. Це цікава особливість прототипного наслідування JavaScript.

this з гетером або сетером

Знову ж таки, цей самий принцип діє, коли функція викликається з гетера чи сетера. У функції, що використовується в якості гетера чи сетера, this прив'язується до об'єкта, де властивість отримується чи встановлюється.

function sum() {
  return this.a + this.b + this.c;
}

var o = {
  a: 1,
  b: 2,
  c: 3,
  get average() {
    return (this.a + this.b + this.c) / 3;
  }
};

Object.defineProperty(o, 'sum', {
    get: sum, enumerable: true, configurable: true});

console.log(o.average, o.sum); // 2, 6

Як конструктор

Коли функція використовується як конструктор (з ключовим словом new), її this прив'язується до нового об'єкта, що створюється.

Хоча за замовчуванням конструктор повертає об'єкт, на який посилається this, він може, натомість, повернути якийсь інший об'єкт (якщо значення, що повертається, не є об'єктом, тоді повертається об'єкт this).

/*
 * Конструктори працюють так:
 *
 * function MyConstructor(){
 *   // Тут розташований код тіла функції.
 *   // Створюємо бажані властивості |this|
 *   // присвоєнням. Наприклад,
 *   this.fum = "nom";
 *   // і так далі...
 *
 *   // Якщо функція має оператор return, який
 *   // повертає об'єкт, цей об'єкт буде
 *   // результатом виразу |new|. Інакше,
 *   // результатом виразу буде поточний об'єкт,
 *   // прив'язаний до |this|
 *   // (найбільш розповсюджений варіант).
 * }
 */

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

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


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

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

У останньому прикладі (C2), через те, що під час створення був повернений об'єкт, новий об'єкт, до якого було прив'язано this, просто відкидається. (Це, по суті, робить інструкцію "this.a = 37;" мертвим кодом. Він не абсолютно мертвий, бо він виконується, але його можна видалити без жодних наслідків для зовнішнього коду.)

Як обробник подій у DOM

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

// Коли викликається як прослуховувач, робить відповідний елемент синім
function bluify(e) {
  // Завжди true
  console.log(this === e.currentTarget);
  // true, коли currentTarget і target є тим самим об'єктом
  console.log(this === e.target);
  this.style.backgroundColor = '#A5D9F3';
}

// Отримати список усіх елементів у document
var elements = document.getElementsByTagName('*');

// Додати bluify як прослуховувач події натискання, щоб, коли
// на елемент натискають, він ставав синім
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 присвоюється таким чином:

<button onclick="alert((function() { return this; })());">
  Показати this внутрішньої функції
</button>

У цьому випадку, this внутрішньої функції не присвоюється, тому функція повертає глобальний об'єкт/window (тобто, об'єкт за замовчуванням у нестрогому режимі, де this не встановлюється викликом).

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

Специфікація Статус Коментар
ECMAScript Latest Draft (ECMA-262)
The definition of 'The this keyword' in that specification.
Draft
ECMAScript 2015 (6th Edition, ECMA-262)
The definition of 'The this keyword' in that specification.
Standard
ECMAScript 5.1 (ECMA-262)
The definition of 'The this keyword' in that specification.
Standard
ECMAScript 3rd Edition (ECMA-262)
The definition of 'The this keyword' in that specification.
Standard
ECMAScript 1st Edition (ECMA-262)
The definition of 'The this keyword' in that specification.
Standard Початкове визначення. Реалізоване у JavaScript 1.0.

Сумісність з веб-переглядачами

Update compatibility data on GitHub
DesktopMobileServer
ChromeEdgeFirefoxInternet ExplorerOperaSafariAndroid webviewChrome for AndroidFirefox for AndroidOpera for AndroidSafari on iOSSamsung InternetNode.js
thisChrome Full support 1Edge Full support 12Firefox Full support 1IE Full support 4Opera Full support YesSafari Full support YesWebView Android Full support 1Chrome Android Full support 18Firefox Android Full support 4Opera Android Full support YesSafari iOS Full support YesSamsung Internet Android Full support 1.0nodejs Full support Yes

Legend

Full support  
Full support

Див. також