JavaScript: ключове слово this для початківців

348

Від автора: іноді буває складно пояснити поняття ключового слова JavaScript this, а також для чого воно потрібно. Але існує п’ять загальних правил, з допомогою яких можна визначити рамки this. Є випадки, які не підходять під ці правила, але вони покривають більшу частину… приступимо!

Значення this зазвичай визначається контекстом виконання функцій. Контекст виконання – це те, як функція викликається.

Важливо знати, що this може бути різним (відсилати на щось інше) для кожного виклику функції.

Нічого страшного, якщо №1 і №2 поки що вам незрозумілі. Вони стануть зрозумілі до кінця статті.

№1 Глобальний об’єкт

Так, визначення ми тепер знаємо. Давайте повернемося на хвилинку. Відкрийте Chrome Developer Console (Windows: Ctrl + Shift + J)(Mac: Cmd + Option + J) та введіть наступне:

console.log(this);

Що вийде?

// Window {…}

Об’єкт window! Тому що в глобальній області видимості this відсилає до глобального об’єкту. У браузері глобальний це об’єкт window.

Щоб ви краще зрозуміли, чому this відсилає до об’єкта window, давайте розглянемо його детальніше. Створіть в консолі нову змінну і надайте їй ім’я:

var myName = ‘Brandon’;

Тепер до цієї змінної можна звертатися через:

myName
// returns -> ‘Brandon’

Але чи знали ви, що всі змінні, оголошені в глобальній області видимості, прив’язані до об’єкта window? Перевіримо це:

window.myName
// returns -> ‘Brandon’
window.myName === myName
// returns -> true

Круто. Тобто раніше коли ми запустили console.log(this) в глобальному контексті, ви знали, що this викликається на глобальному об’єкті. А так як в браузері глобальний об’єкт це this, то тепер зрозуміло, чому:

console.log(this)
// returns -> window{…}

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

function test() {
return this;
}
test()

І знову ключове слово this повертає глобальний об’єкт (window). Все тому що не this межах оголошеного об’єкта, тому береться об’єкт за замовчуванням (window). Поки що концепція може здатися складно, але по мірі прочитання статті все стане зрозуміліше. Зауваження – якщо ви не використовуєте суворий режим, то this в прикладі вище буде undefined.

№2 Оголошений об’єкт

Коли ключове слово this використовується всередині оголошеного об’єкта, значення this встановлюється до найближчого батьківського об’єкта, на якому викликаний метод. Подивіться нижче код, де я визначив об’єкт person і використовував this всередині методу full

var person = {
first: ‘John’,
last: ‘Smith’,
full: function() {
console.log(this.first + ” + this.last);
}
};
person.full();
// logs => ‘John Smith’

Щоб краще проілюструвати зв’язок this з об’єктом person, скопіюйте код нижче в консоль браузера. Код майже як зверху, ми просто виводимо console.log(this), щоб подивитися що повернеться.

var person = {
first: ‘John’,
last: ‘Smith’,
full: function() {
console.log(this);
}
};
person.full();
// logs => Object {first: “John”, last: “Smith”, full: function}

Як бачите, консоль повертає об’єкт person, що підтверджує, що this прийняло значення person.

І ще одне, перш ніж ми продовжимо. Пам’ятайте, що ми сказали, що значення this встановлюється в найближчий батьківський об’єкт викликається методу? А що буде, якщо об’єкти вкладені? Подивіться нижче код. Там у нас об’єкт person з такими ж ключами first, last, full. Однак у цей раз ми вклали об’єкт personTwo. У personTwo ті ж три ключа.

var person = {
first: ‘John’,
last: ‘Smith’,
full: function() {
console.log(this.first + ” + this.last);
},
personTwo: {
first: ‘Allison’,
last: ‘Jones’,
full: function() {
console.log(this.first + ” + this.last);
}
}
};

Що буде, якщо викликати два методу full? Дізнаємося.

person.full();
// logs => ‘John Smith’
person.personTwo.full();
// logs => ‘Allison Jones’

Значення this встановлюється в найближчий батьківський об’єкт викликається методу. При виконанні person.full() всередині функції this обмежений об’єктом person. Однак при виклику person.personTwo.full() всередині функції this обмежений вже об’єктом personTwo!

№3 Ключове слово new

При використанні ключового слова new (конструктор), this обмежений новим створюваним об’єктом.

Розберемо приклад:

function Car(make, model) {
this.make = make;
this.model = model;
};

Ви можете подумати, що this обмежений глобальним об’єктом – і ви будете праві… поки ми не додамо ключове слово new. Якщо використовувати new, то значення this встановлюється в порожній об’єкт. У нашому випадку myCar.

var myCar = new Car(‘Ford’, ‘Escape’);
console.log(myCar);
// logs => Car {make: “Ford”, model: “Escape”}

Потрібно зрозуміти, що робить слово new. Це абсолютно нова тема. Поки що просто якщо бачите new, знайте, що this відсилає до нового порожнього об’єкту.

№4 Call, Bind, Apply

Останнє, але не менш важливе, ми можемо явно задавати значення this через call(), bind() і apply(). Три методи дуже схожі, але важливо зрозуміти їх відмінності.

Call і Apply виконуються відразу ж. Call приймає будь-яку кількість параметрів: this, після якого йдуть додаткові аргументи. Apply приймає тільки два значення: this, після якого йде масив додаткових аргументів.

Ви ще тут? Приклад все прояснить. Подивіться на код нижче ми намагаємося скласти числа. Скопіюйте його в консоль браузера і викличте функцію.

function add(c, d) {
console.log(this.a + this.b + c + d);
}
add(3,4);
// logs =>, NaN

Функція add логирует NaN (не число). Бо this.a і this.b не визначені. Вони не існують. Ви не можете скласти число з чимось невизначеним.

Введемо в рівняння об’єкт. З допомогою call() і apply() ми можемо викликати функцію з нашим об’єктом:

function add(c, d) {
console.log(this.a + this.b + c + d);
}
var ten = {a: 1, b: 2};
add.call(ten, 3, 4);
// logs => 10
add.apply(ten, [3,4]);
// logs => 10

Коли ми використовуємо add.call(), this повинен бути обмежений першим параметром. Інші параметри передаються в викликану функцію. Тому всередині add() this.a відсилає до ten.a ten.b, а нам повертається 1+2+3+4 або 10.

Add.apply() працює так само. Перший параметр обмежує this. Другий параметр – масив аргументів для функції.

А Bidn? Параметри bind() ідентичні call(), але bind() не виконується негайно. bind() повертає функцію з контекстом this. Тому bind() корисний, коли ми не знаємо всі аргументи. Приклад допоможе прояснити:

var small = {
a: 1,
go: function(b,c,d){
console.log(this.a+b+c+d);
}
}
var large = {
a: 100
}

Скопіюйте код вище в консоль і виконайте наступне.

small.go(2,3,4);
// logs 1+2+3+4 => 10

Круто. Все по старому. А що якщо використовувати значення large.a? Можна використовувати call/apply:

small.go.call(large,2,3,4);
// logs 100+2+3+4 => 109

А що якщо ми ще не знаємо всі три аргументи? Можна взяти bind:

var bindTest = small.go.bind(large,2);

Якщо вивести в консолі console.log нашу змінну вище bindTest, ми побачимо, з чим працюємо

console.log(bindTest);
// logs => function (b,c,d){console.log(this.a+b+c+d);}

Пам’ятайте, що з bind повертається функція, у якої вже є свій this! Тому наш this обмежений об’єктом large. Ми також передали наш другий аргумент 2. Як тільки ми дізнаємося інші аргументи, ми можемо їх передати:

bindTest(3,4);
// logs 100+2+3+4 => 109

Весь код одним блоком. Подивіться його і скопіюйте в консоль, щоб розібратися!

var small = {
a: 1,
go: function(b,c,d){
console.log(this.a+b+c+d);
}
}
var large = {
a: 100
}
small.go(2,3,4);
// logs 1+2+3+4 => 10
var bindTest = small.go.bind(large,2);
console.log(bindTest);
// logs => function (b,c,d){console.log(this.a+b+c+d);}
bindTest(3,4);
// logs 100+2+3+4 => 109

№5 Стрілочні функції

Це настільки велика тема, що я написав цілу статтю Arrow Functions for Beginners

Висновок

У вас вийшло! Тепер в більшості випадків ви зможете зрозуміти, до чого відсилає this! Запам’ятайте кілька речей:

Значення this зазвичай визначається контекстом виклику функцій.

В глобальній області видимості this відсилає до глобального об’єкту (window).

При використанні new (конструктор), this обмежений новим створюваним об’єктом.

Значення this можна явно задати через call(), bind() і apply()

Стрілочні функції не прив’язують this – this обмежується лексично (тобто за оригінальним контексту)