Ключове слово функції this
поводиться дещо інакше у JavaScript у порівнянні з іншими мовами. Воно також має певні відмінності між строгим та нестрогим режимами.
У більшості випадків, значення this
визначається тим, як викликається функція (прив'язка під час виконання). Воно не може бути переприсвоєне під час виконання, і може змінюватись кожен раз, коли викликається функція. ES5 запровадив метод bind()
для присвоєння значення this у функції незалежно від того, як вона викликається, а ES2015 запровадив стрілкові функції, які не мають власної прив'язки this
(вони зберігають значення this
оточуючого лексичного контексту).
The source for this interactive example is stored in a GitHub repository. If you'd like to contribute to the interactive examples project, please clone https://github.com/mdn/interactive-examples and send us a pull request.
Синтаксис
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 (ECMA-262) The definition of 'The this keyword' in that specification. |
Living Standard | |
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. |
Сумісність з веб-переглядачами
BCD tables only load in the browser
Див. також
- Строгий режим
- Лагідне роз'яснення ключового слова 'this' у JavaScript
- Отримання глобального контексту:
globalThis