JavaScript में this
की वैल्यू का पता लगाना मुश्किल हो सकता है. इसे पता लगाने का तरीका यहां बताया गया है…
JavaScript का this
कई चुटकुलों का हिस्सा है और ऐसा इसलिए, क्योंकि यह काफ़ी जटिल है.
हालांकि, मैंने डेवलपर को इस this
से बचने के लिए, डोमेन के हिसाब से बहुत ज़्यादा मुश्किल और खास चीज़ें करते देखा है. अगर आपको this
के बारे में नहीं पता है, तो उम्मीद है कि इससे आपको मदद मिलेगी. यह मेरी this
गाइड है.
हम सबसे खास स्थिति से शुरू करेंगे और सबसे कम खास स्थिति पर खत्म करेंगे. यह लेख एक बड़े if (…) … else if () … else if (…) …
की तरह है. इससे आपको सीधे उस कोड के पहले सेक्शन पर जाने में मदद मिलेगी जो आपको दिख रहा है.
- अगर फ़ंक्शन को ऐरो फ़ंक्शन के तौर पर बताया गया है
- अगर फ़ंक्शन/क्लास को
new
के साथ कॉल किया जाता है, तो - अगर फ़ंक्शन में 'बाउंड'
this
वैल्यू है, तो - अगर
this
को कॉल के समय पर सेट किया गया है, तो - अगर फ़ंक्शन को पैरंट ऑब्जेक्ट (
parent.func()
) के ज़रिए कॉल किया जाता है, तो - अगर फ़ंक्शन या पैरंट स्कोप, स्ट्रिक्ट मोड में है, तो
- अन्य मामलों में
अगर फ़ंक्शन को ऐरो फ़ंक्शन के तौर पर बताया गया है:
const arrowFunction = () => {
console.log(this);
};
इस मामले में, this
की वैल्यू हमेशा वही है जो पैरंट स्कोप में this
है:
const outerThis = this;
const arrowFunction = () => {
// Always logs `true`:
console.log(this === outerThis);
};
ऐरो फ़ंक्शन बहुत अच्छे होते हैं, क्योंकि this
की अंदरूनी वैल्यू को बदला नहीं जा सकता. यह हमेशा बाहरी this
जैसी ही होती है.
अन्य उदाहरण
ऐरो फ़ंक्शन से, this
की वैल्यू को bind
से नहीं बदला जा सकता:
// Logs `true` - bound `this` value is ignored:
arrowFunction.bind({foo: 'bar'})();
ऐरो फ़ंक्शन की मदद से, this
की वैल्यू को call
या apply
से बदला नहीं जा सकता:
// Logs `true` - called `this` value is ignored:
arrowFunction.call({foo: 'bar'});
// Logs `true` - applied `this` value is ignored:
arrowFunction.apply({foo: 'bar'});
ऐरो फ़ंक्शन की मदद से, किसी दूसरे ऑब्जेक्ट के सदस्य के तौर पर फ़ंक्शन को कॉल करके, this
की वैल्यू नहीं बदली जा सकती:
const obj = {arrowFunction};
// Logs `true` - parent object is ignored:
obj.arrowFunction();
ऐरो फ़ंक्शन के साथ, फ़ंक्शन को कॉन्स्ट्रक्टर के तौर पर कॉल करके, this
की वैल्यू नहीं बदली जा सकती:
// TypeError: arrowFunction is not a constructor
new arrowFunction();
'बाउंड' इंस्टेंस के तरीके
इंस्टेंस तरीकों की मदद से, अगर आपको यह पक्का करना है कि this
हमेशा क्लास इंस्टेंस का इस्तेमाल करता है, तो सबसे सही तरीका यह है कि ऐरो फ़ंक्शन और क्लास फ़ील्ड का इस्तेमाल किया जाए:
class Whatever {
someMethod = () => {
// Always the instance of Whatever:
console.log(this);
};
}
यह पैटर्न, कॉम्पोनेंट (जैसे, React कॉम्पोनेंट या वेब कॉम्पोनेंट) में इवेंट के लिसनर के तौर पर इंस्टेंस मेथड का इस्तेमाल करते समय काफ़ी मददगार होता है.
ऊपर दिए गए कोड से ऐसा लग सकता है कि यह "this
, पैरंट स्कोप में मौजूद this
जैसा ही होगा" के नियम का उल्लंघन करता है. हालांकि, अगर कन्स्ट्रक्टर में चीज़ों को सेट करने के लिए, क्लास फ़ील्ड को सिंटैक्टिक शुगर के तौर पर देखा जाए, तो यह समझ में आने लगता है:
class Whatever {
someMethod = (() => {
const outerThis = this;
return () => {
// Always logs `true`:
console.log(this === outerThis);
};
})();
}
// …is roughly equivalent to:
class Whatever {
constructor() {
const outerThis = this;
this.someMethod = () => {
// Always logs `true`:
console.log(this === outerThis);
};
}
}
वैकल्पिक पैच में, कंस्ट्रक्टर में किसी मौजूदा फ़ंक्शन को बाइंड करना या कंस्ट्रक्टर में फ़ंक्शन असाइन करना शामिल होता है. अगर किसी वजह से क्लास फ़ील्ड का इस्तेमाल नहीं किया जा सकता, तो कंस्ट्रक्टर में फ़ंक्शन असाइन करना एक अच्छा विकल्प है:
class Whatever {
constructor() {
this.someMethod = () => {
// …
};
}
}
या फिर, अगर फ़ंक्शन/क्लास को new
के साथ कॉल किया जाता है:
new Whatever();
ऊपर दिए गए कोड में, Whatever
(या अगर यह क्लास है, तो उसका कंस्ट्रक्टर फ़ंक्शन) को this
के तौर पर सेट किया जाएगा, जो Object.create(Whatever.prototype)
के नतीजे पर सेट होगा.
class MyClass {
constructor() {
console.log(
this.constructor === Object.create(MyClass.prototype).constructor,
);
}
}
// Logs `true`:
new MyClass();
पुराने स्टाइल के कन्स्ट्रक्टर पर भी यही बात लागू होती है:
function MyClass() {
console.log(
this.constructor === Object.create(MyClass.prototype).constructor,
);
}
// Logs `true`:
new MyClass();
अन्य उदाहरण
new
के साथ कॉल करने पर, this
की वैल्यू को bind
से बदला नहीं जा सकता:
const BoundMyClass = MyClass.bind({foo: 'bar'});
// Logs `true` - bound `this` value is ignored:
new BoundMyClass();
new
के साथ कॉल करने पर, फ़ंक्शन को किसी दूसरे ऑब्जेक्ट के सदस्य के तौर पर कॉल करके, this
की वैल्यू नहीं बदली जा सकती:
const obj = {MyClass};
// Logs `true` - parent object is ignored:
new obj.MyClass();
अगर फ़ंक्शन में 'बाउंड' this
वैल्यू है, तो:
function someFunction() {
return this;
}
const boundObject = {hello: 'world'};
const boundFunction = someFunction.bind(boundObject);
जब भी boundFunction
को कॉल किया जाएगा, तब उसकी this
वैल्यू, bind
(boundObject
) को पास किया गया ऑब्जेक्ट होगा.
// Logs `false`:
console.log(someFunction() === boundObject);
// Logs `true`:
console.log(boundFunction() === boundObject);
अन्य उदाहरण
किसी बाउंड फ़ंक्शन को कॉल करते समय, this
की वैल्यू को call
या
apply
से नहीं बदला जा सकता:
// Logs `true` - called `this` value is ignored:
console.log(boundFunction.call({foo: 'bar'}) === boundObject);
// Logs `true` - applied `this` value is ignored:
console.log(boundFunction.apply({foo: 'bar'}) === boundObject);
बाउंड किए गए फ़ंक्शन को कॉल करते समय, फ़ंक्शन को किसी दूसरे ऑब्जेक्ट के सदस्य के तौर पर कॉल करके, this
की वैल्यू नहीं बदली जा सकती:
const obj = {boundFunction};
// Logs `true` - parent object is ignored:
console.log(obj.boundFunction() === boundObject);
अगर this
को कॉल के समय पर सेट किया गया है, तो:
function someFunction() {
return this;
}
const someObject = {hello: 'world'};
// Logs `true`:
console.log(someFunction.call(someObject) === someObject);
// Logs `true`:
console.log(someFunction.apply(someObject) === someObject);
this
की वैल्यू, call
/apply
को पास किया गया ऑब्जेक्ट है.
माफ़ करें, डीओएम इवेंट लिसनर जैसी चीज़ों की वजह से this
किसी दूसरी वैल्यू पर सेट हो जाता है. इसका इस्तेमाल करने पर, कोड को समझना मुश्किल हो सकता है:
element.addEventListener('click', function (event) { // Logs `element`, since the DOM spec sets `this` to // the element the handler is attached to. console.log(this); });
मैं ऊपर दिए गए मामलों में this
का इस्तेमाल नहीं करता/करती. इसके बजाय:
element.addEventListener('click', (event) => { // Ideally, grab it from a parent scope: console.log(element); // But if you can't do that, get it from the event object: console.log(event.currentTarget); });
इसके अलावा, अगर फ़ंक्शन को पैरंट ऑब्जेक्ट (parent.func()
) के ज़रिए कॉल किया जाता है, तो:
const obj = {
someMethod() {
return this;
},
};
// Logs `true`:
console.log(obj.someMethod() === obj);
इस मामले में, फ़ंक्शन को obj
के सदस्य के तौर पर कॉल किया जाता है, इसलिए this
obj
होगा. ऐसा कॉल-टाइम पर होता है. इसलिए, अगर फ़ंक्शन को उसके पैरंट ऑब्जेक्ट के बिना या किसी दूसरे पैरंट ऑब्जेक्ट के साथ कॉल किया जाता है, तो लिंक टूट जाता है:
const {someMethod} = obj;
// Logs `false`:
console.log(someMethod() === obj);
const anotherObj = {someMethod};
// Logs `false`:
console.log(anotherObj.someMethod() === obj);
// Logs `true`:
console.log(anotherObj.someMethod() === anotherObj);
someMethod() === obj
गलत है, क्योंकि someMethod
को obj
का सदस्य नहीं कहा जाता है. ऐसा हो सकता है कि आपने कुछ ऐसा करने की कोशिश की हो, जिससे आपको यह गड़बड़ी मिली हो:
const $ = document.querySelector;
// TypeError: Illegal invocation
const el = $('.some-element');
ऐसा इसलिए होता है, क्योंकि querySelector
को लागू करने पर, वह अपनी this
वैल्यू देखता है और उम्मीद करता है कि वह किसी तरह का डीओएम नोड हो. ऊपर दिए गए उदाहरण में, यह कनेक्शन नहीं होता. ऊपर बताए गए लक्ष्य को पूरा करने के लिए:
const $ = document.querySelector.bind(document);
// Or:
const $ = (...args) => document.querySelector(...args);
मज़ेदार जानकारी: सभी एपीआई अपनी कंपनी में this
का इस्तेमाल नहीं करते. console.log
जैसे Console के तरीकों को बदला गया था, ताकि this
रेफ़रंस से बचा जा सके. इसलिए, log
को console
से बंधे होने की ज़रूरत नहीं है.
अगर फ़ंक्शन या पैरंट स्कोप, स्ट्रिक्ट मोड में है, तो:
function someFunction() {
'use strict';
return this;
}
// Logs `true`:
console.log(someFunction() === undefined);
इस मामले में, this
की वैल्यू तय नहीं है. अगर पैरंट स्कोप स्ट्रिक्ट मोड में है और सभी मॉड्यूल स्ट्रिक्ट मोड में हैं, तो फ़ंक्शन में 'use strict'
की ज़रूरत नहीं है.
अन्यथा:
function someFunction() {
return this;
}
// Logs `true`:
console.log(someFunction() === globalThis);
इस मामले में, this
की वैल्यू और globalThis
एक ही है.
वाह!
यह बहुत आसान है! मुझे this
के बारे में बस इतना ही पता है. क्या आपका कोई सवाल है? क्या मुझे कुछ पता नहीं है? मुझे ट्वीट करें.
समीक्षा करने के लिए, मैथियास बीन्स, इंगवार स्टीफ़नीन, और थॉमस स्टाइनर का धन्यवाद.