this Keyword & Execution Context
"this բանալի բառը JavaScript-ի ամենակարևոր, բայց միևնույն ժամանակ ամենաշատ շփոթմունք առաջացնող հասկացություններից մեկն է։"
Ի՞նչ է this-ը (Մենթալ Մոդել)
this-ը JavaScript-ում փոփոխական չէ և կապված չէ այն scope-ի հետ, թե որտեղ է ֆունկցիան գրված։
Ոսկե կանոն:
this-ի արժեքը որոշվում է ֆունկցիայի կանչման պահին (Dynamic Binding)՝ կախված նրանից, թե ԻՆՉՊԵՍ է այն կանչվել, այլ ոչ թե ՈՐՏԵՂ է հայտարարված:
this-ի 4 Հիմնական Կանոնները (Binding Rules)
JavaScript-ում this-ի արժեքը որոշելու համար գործում է 4 հիմնական կանոն՝ ըստ առաջնահերթության (Priority):
1. Explicit Binding (Բացահայտ կապում՝ call, apply, bind)
Ամենաբարձր առաջնահերթությունն է։ Մենք ինքներս ենք ստիպողաբար ֆունկցիային ասում, թե ինչ լինի this-ի արժեքը։
call(thisArg, arg1, arg2...)— Կանչում է ֆունկցիան անմիջապես՝ փոխանցելովthis-ը և արգումենտները հատ-հատ։apply(thisArg, [args])— Նույնն է, ինչcall-ը, բայց արգումենտները փոխանցվում են զանգվածով (array)։bind(thisArg, arg1...)— Չի կանչում ֆունկցիան անմիջապես, այլ վերադարձնում է նոր ֆունկցիա՝ պինդ կապված (bound)this-ով։
function greet(greeting, punctuation) {
console.log(`${greeting}, ${this.name}${punctuation}`);
}
const user = { name: 'Hayk' };
// Call & Apply
greet.call(user, 'Hello', '!'); // Output: "Hello, Hayk!"
greet.apply(user, ['Hi', '.']); // Output: "Hi, Hayk."
// Bind
const boundGreet = greet.bind(user, 'Welcome');
boundGreet('!'); // Output: "Welcome, Hayk!"
2. Implicit Binding (Անուղղակի / Օբյեկտային կապում)
Եթե ֆունկցիան կանչվում է որպես օբյեկտի մեթոդ (obj.method()), this-ը դառնում է կետից (dot) ձախ գտնվող օբյեկտը։
const user = {
name: 'Ani',
showName() {
console.log(this.name);
}
};
user.showName(); // `this`-ը `user` օբյեկտն է -> Output: "Ani"
⚠️ Կարևոր վտանգ (Loss of Implicit Binding / «Կորսված this»):
Եթե մեթոդը հանում ենք օբյեկտից և վերագրում փոփոխականի կամ փոխանցում որպես callback, կապը կորչում է։
const fn = user.showName;
fn(); // undefined (կամ TypeError strict mode-ում), որովհետև կանչի պահին կետ չկա!
3. New Binding (Կոնստրուկտորի կապում)
Երբ ֆունկցիան կանչվում է new օպերատորով (new User()), JavaScript-ը ետնաբեմում կատարում է հետևյալ քայլերը․
- Ստեղծում է նոր դատարկ օբյեկտ
{}։ this-ը կապում է այդ նոր ստեղծված օբյեկտին։- Կապում է նոր օբյեկտի prototype-ը ֆունկցիայի
prototypeհղման հետ։ - Վերադարձնում է այդ օբյեկտը (եթե ֆունկցիան explicit ուրիշ օբյեկտ չի վերադարձնում)։
function Person(name) {
// 1. this = {}; (աննկատ)
this.name = name;
// 3. return this; (աննկատ)
}
const dev = new Person('Dave');
console.log(dev.name); // Output: "Dave"
4. Default Binding (Լռելյայն կապում)
Եթե նախորդ 3 կանոններից և ոչ մեկը չի համապատասխանում (սովորական ֆունկցիայի կանչ առանց կետի)․
- Non-strict mode:
this-ը հղվում է գլոբալ օբյեկտին (windowբրաուզերում,globalNode.js-ում)։ - Strict mode (
'use strict'):this-ը ստանում էundefinedարժեք։
function show() {
'use strict';
console.log(this);
}
show(); // Output: undefined
Arrow Functions (Սլաքային ֆունկցիաներ) և this
Սլաքային ֆունկցիաները չունեն իրենց սեփական this-ը։
Նրանք օգտագործում են Lexical this — վերցնում են this-ի արժեքը այն արտաքին scope-ից, որտեղ ստեղծվել են։
call(), apply() կամ bind() մեթոդները չեն կարող փոխել սլաքային ֆունկցիայի this-ը։
const timer = {
name: 'Counter',
start() {
// Սովորական ֆունկցիայի մեջ this-ը `timer`-ն է
setTimeout(() => {
// Arrow function-ը վերցնում է `this`-ը start()-ից
console.log(this.name);
}, 1000);
}
};
timer.start(); // 1 վայրկյան անց տպում է "Counter"
Summary Checklist: Ինչպես որոշել this-ը 5 վայրկյանում
- 🎯 Արդյո՞ք սլաքային ֆունկցիա է (
=>):
👉 ԱՅՈ: Look outside! Վերցնում է արտաքին scope-իthis-ը։ - 🏗️ Արդյո՞ք կանչվել է
new-ով:
👉 ԱՅՈ:this-ը ստեղծվող նոր օբյեկտն է։ - ⚙️ Արդյո՞ք կանչվել է
call,applyկամbind-ով:
👉 ԱՅՈ:this-ը փոխանցված առաջին արգումենտն է։ - 📦 Արդյո՞ք կանչվել է օբյեկտի միջոցով (
obj.method()):
👉 ԱՅՈ:this-ը կետից ձախ գտնվող օբյեկտն է (obj)։ - 🌐 Սովորական կանչ է (
fn()):
👉 Strict mode-ում ➔undefined
👉 Non-strict mode-ում ➔window/global
Գործնական Խնդիրներ (Self-Quiz)
Խնդիր 1: Arrow Function vs Regular Function
const obj = {
a: 10,
b: function() {
console.log(this.a);
},
c: () => {
console.log(this.a);
}
};
obj.b(); // ❓
obj.c(); // ❓
Պատասխան:
• obj.b() ➔ 10 (Implicit Binding: this-ը obj-ն է)
• obj.c() ➔ undefined (Arrow function-ի արտաքին scope-ը global scope-ն է)
Խնդիր 2: Context-ի կորուստ Callback-ում
const user = {
name: 'Hayk',
greet() {
console.log(`Hi, ${this.name}`);
}
};
setTimeout(user.greet, 500); // ❓
Պատասխան: Hi, undefined (կամ սխալ strict mode-ում), որովհետև setTimeout-ին փոխանցվել է ֆունկցիայի հղումը, և այն կանչվելու է առանց context-ի։
Լուծումներ:
1. setTimeout(user.greet.bind(user), 500)
2. setTimeout(() => user.greet(), 500)