किसी फ़ंक्शन में "यह" कीवर्ड कैसे काम करता है?


248

मैं अभी जावास्क्रिप्ट में एक दिलचस्प स्थिति में आया हूं। मेरे पास एक विधि है जिसमें ऑब्जेक्ट-शाब्दिक संकेतन का उपयोग करके कई वस्तुओं को परिभाषित किया गया है। उन वस्तुओं के अंदर, thisसूचक का उपयोग किया जा रहा है। कार्यक्रम के व्यवहार से, मैंने यह माना है कि thisपॉइंटर उस वर्ग का उल्लेख कर रहा है जिस पर विधि लागू की गई थी, न कि वह वस्तु जो शाब्दिक रूप से बनाई जा रही हो।

यह मनमाना लगता है, हालांकि यह वह तरीका है जिससे मैं काम करने की उम्मीद करूंगा। क्या यह परिभाषित व्यवहार है? क्या यह क्रॉस-ब्राउज़र सुरक्षित है? क्या कोई तर्क अंतर्निहित है कि यह ऐसा क्यों है जो "कल्पना ऐसा कहता है" से परे है (उदाहरण के लिए, क्या यह कुछ व्यापक डिजाइन निर्णय / दर्शन का परिणाम है)? परेड-डाउन कोड उदाहरण:

// inside class definition, itself an object literal, we have this function:
onRender: function() {

    this.menuItems = this.menuItems.concat([
        {
            text: 'Group by Module',
            rptletdiv: this
        },
        {
            text: 'Group by Status',
            rptletdiv: this
        }]);
    // etc
}

ऐसा होता है जब मैं यह भी करता var signup = { onLoadHandler:function(){ console.log(this); return Type.createDelegate(this,this._onLoad); }, _onLoad: function (s, a) { console.log("this",this); }};
हूं


बाहर की जाँच इस पोस्ट । इस खोजशब्द के विभिन्न उपयोग और व्यवहारों की कुछ अच्छी व्याख्या है।
लव हसीजा

इस लिंक को चेक करें scotch.io/@alZami/understanding-this-in-javascript
AL-zami

जवाबों:


558

मेरी एक और पोस्ट से नरभक्षी, यहाँ आप से अधिक इस बारे में जानना चाहता था ।

शुरू करने से पहले, यहाँ सबसे महत्वपूर्ण बात यह है कि जावास्क्रिप्ट के बारे में ध्यान रखें, और जब यह समझ में न आए तो अपने आप को दोहराएं। जावास्क्रिप्ट कक्षाएं नहीं है (ES6 classहै वाक्यात्मक चीनी )। यदि कुछ एक वर्ग की तरह दिखता है, तो यह एक चतुर चाल है। जावास्क्रिप्ट में ऑब्जेक्ट्स और फ़ंक्शंस हैं । (यह 100% सटीक नहीं है, फ़ंक्शन सिर्फ ऑब्जेक्ट हैं, लेकिन कभी-कभी उन्हें अलग-अलग चीजों के रूप में सोचने में मददगार हो सकता है)

इस चर कार्यों से जुड़ी है। जब भी आप किसी फ़ंक्शन को लागू करते हैं, तो यह एक निश्चित मान दिया जाता है , इस पर निर्भर करता है कि आप फ़ंक्शन को कैसे लागू करते हैं। इसे अक्सर आह्वान पैटर्न कहा जाता है।

जावास्क्रिप्ट में फ़ंक्शन को लागू करने के चार तरीके हैं। आप फ़ंक्शन को एक विधि के रूप में, एक फ़ंक्शन के रूप में, एक निर्माता के रूप में और आवेदन के साथ आह्वान कर सकते हैं ।

एक विधि के रूप में

एक विधि एक फ़ंक्शन है जो किसी ऑब्जेक्ट से जुड़ी होती है

var foo = {};
foo.someMethod = function(){
    alert(this);
}

जब एक विधि के रूप में लागू किया जाता है, तो यह फ़ंक्शन / विधि का एक हिस्सा होता है। इस उदाहरण में, यह फू करने के लिए बाध्य होगा।

एक समारोह के रूप में

यदि आपके पास एक अकेला कार्य है, तो यह चर "वैश्विक" ऑब्जेक्ट के लिए बाध्य होगा, लगभग हमेशा एक ब्राउज़र के संदर्भ में विंडो ऑब्जेक्ट।

 var foo = function(){
    alert(this);
 }
 foo();

यह वही हो सकता है जो आपको ट्रिप कर रहा है , लेकिन बुरा न मानें। कई लोग इसे एक खराब डिजाइन निर्णय मानते हैं। चूँकि कॉलबैक को एक फंक्शन के रूप में लिया जाता है, न कि एक विधि के रूप में, इसीलिए आप देख रहे हैं कि जो असंगत व्यवहार प्रतीत होता है।

बहुत से लोग कुछ ऐसा करके समस्या के आस-पास पहुँच जाते हैं, जैसे, यह

var foo = {};
foo.someMethod = function (){
    var that=this;
    function bar(){
        alert(that);
    }
}

आप एक चर को परिभाषित करते हैं जो इस ओर इशारा करता है । बंद करना (एक विषय जो उसका अपना है) वह चारों ओर रखता है , इसलिए यदि आप बार को कॉलबैक कहते हैं, तो इसका संदर्भ अभी भी है।

नोट: यदि use strictमोड में फ़ंक्शन के रूप में उपयोग किया जाता है, thisतो वैश्विक के लिए बाध्य नहीं है। (यह है undefined)।

एक कंस्ट्रक्टर के रूप में

आप एक फ़ंक्शन को एक निर्माता के रूप में भी आमंत्रित कर सकते हैं। आपके द्वारा उपयोग किए जा रहे नामकरण परंपरा के आधार पर (TestObject) यह भी हो सकता है कि आप क्या कर रहे हैं और क्या आप को ट्रिप कर रहे हैं

आप नए कीवर्ड के साथ कंस्ट्रक्टर के रूप में एक फंक्शन शुरू करते हैं।

function Foo(){
    this.confusing = 'hell yeah';
}
var myObject = new Foo();

जब एक निर्माता के रूप में आमंत्रित किया जाता है, तो एक नया ऑब्जेक्ट बनाया जाएगा, और यह उस ऑब्जेक्ट के लिए बाध्य होगा। फिर, यदि आपके पास आंतरिक फ़ंक्शन हैं और उन्हें कॉलबैक के रूप में उपयोग किया जाता है, तो आप उन्हें फ़ंक्शन के रूप में आमंत्रित करेंगे, और यह वैश्विक ऑब्जेक्ट के लिए बाध्य होगा। उस var का उपयोग करें = यह ट्रिक / पैटर्न।

कुछ लोगों को लगता है कि कंस्ट्रक्टर / नया कीवर्ड जावा / पारंपरिक ओओपी प्रोग्रामर के लिए फेंका गया एक हड्डी था जो कक्षाओं के समान कुछ बनाने के लिए था।

अप्लाई विधि के साथ

अंत में, प्रत्येक फ़ंक्शन में एक विधि है (हाँ, फ़ंक्शन जावास्क्रिप्ट में ऑब्जेक्ट हैं) "लागू करें" नाम दिया गया है। लागू करें आप यह निर्धारित क्या की मूल्य की सुविधा देता है यह हो जाएगा, और भी आप तर्क की एक सरणी में पारित करने देता है। यहाँ एक बेकार उदाहरण है।

function foo(a,b){
    alert(a);
    alert(b);
    alert(this);
}
var args = ['ah','be'];
foo.apply('omg',args);

8
नोट: सख्त मोड में , फ़ंक्शन इनवोकेशन के लिए thisहोगा undefined
Miscreant

1
एक फ़ंक्शन घोषणा, उदा। समारोह myfunction () {}, "एक विधि" के रूप में एक विशेष मामला है जहां "यह" वैश्विक क्षेत्र (खिड़की) है।
रिचर्ड

1
@ श्रीखंड: सख्त मोड को छोड़कर, और thisगुंजाइश के साथ कुछ नहीं करना है। आपको वैश्विक वस्तु से मतलब है ।
टीजे क्राउडर

@ एलन-तूफान। "एक निर्माता के रूप में" के मामले में क्या this.confusing = 'hell yeah';ऐसा ही है var confusing = 'hell yeah';? तो दोनों अनुमति देंगे myObject.confusing? यह अच्छा होगा यदि ऐसा न हो कि आप thisआंतरिक कार्य के लिए गुणों और अन्य चर बनाने के लिए उपयोग कर सकें।
वुंथ

लेकिन उसके बाद फिर से मुझे लगता है कि जो काम सामान समारोह और निर्माता के माध्यम से पारित मूल्य के बाहर किया जा सकता है: function Foo(thought){ this.confusing = thought; }और उसके बादvar myObject = new Foo("hell yeah");
wunth

35

फंक्शन कॉल

कार्य केवल एक प्रकार की वस्तु है।

सभी फ़ंक्शन ऑब्जेक्ट्स में कॉल और लागू करने के तरीके होते हैं जो उन फ़ंक्शन ऑब्जेक्ट को निष्पादित करते हैं जो उन्हें कहा जाता है।

जब कहा जाता है, तो इन विधियों का पहला तर्क उस ऑब्जेक्ट को निर्दिष्ट करता है जिसे thisफ़ंक्शन द्वारा निष्पादन के दौरान फ़ंक्शन द्वारा संदर्भित किया जाएगा - यदि यह nullया undefined, वैश्विक ऑब्जेक्ट, के windowलिए उपयोग किया जाता है this

इस प्रकार, एक समारोह बुला ...

whereAmI = "window";

function foo()
{
    return "this is " + this.whereAmI + " with " + arguments.length + " + arguments";
}

... कोष्ठक के साथ - foo()- foo.call(undefined)या के बराबर है foo.apply(undefined), जो प्रभावी रूप से foo.call(window)या के समान है foo.apply(window)

>>> foo()
"this is window with 0 arguments"
>>> foo.call()
"this is window with 0 arguments"

अतिरिक्त तर्कों को callफ़ंक्शन कॉल के तर्क के रूप में पारित किया जाता है, जबकि फ़ंक्शन कॉल के लिए तर्क को applyनिर्दिष्ट करने के लिए एक एकल अतिरिक्त तर्क एक अर्रे जैसी वस्तु के रूप में निर्दिष्ट कर सकता है।

इस प्रकार, foo(1, 2, 3)के बराबर है foo.call(null, 1, 2, 3)या foo.apply(null, [1, 2, 3])

>>> foo(1, 2, 3)
"this is window with 3 arguments"
>>> foo.apply(null, [1, 2, 3])
"this is window with 3 arguments"

यदि कोई कार्य किसी वस्तु का गुण है ...

var obj =
{
    whereAmI: "obj",
    foo: foo
};

... वस्तु के माध्यम से समारोह के लिए एक संदर्भ पहुँचने और कोष्ठकों के साथ यह बुला - obj.foo()- के बराबर है foo.call(obj)या foo.apply(obj)

हालांकि, वस्तुओं के गुणों के रूप में आयोजित कार्य उन वस्तुओं के लिए "बाध्य" नहीं हैं। जैसा कि आप objऊपर की परिभाषा में देख सकते हैं , क्योंकि फ़ंक्शंस केवल एक प्रकार की वस्तु हैं, उन्हें संदर्भित किया जा सकता है (और इसे फ़ंक्शन कॉल के संदर्भ में पास किया जा सकता है या फ़ंक्शन कॉल से संदर्भ द्वारा लौटाया जा सकता है)। जब किसी फ़ंक्शन का संदर्भ पास किया जाता है, तो इसके बारे में कोई अतिरिक्त जानकारी नहीं दी जाती है कि इसे कहाँ से ले जाया गया है, जिसके कारण निम्नलिखित होता है:

>>> baz = obj.foo;
>>> baz();
"this is window with 0 arguments"

हमारे फ़ंक्शन संदर्भ bazके लिए कॉल, कॉल के लिए कोई संदर्भ प्रदान नहीं करता है, इसलिए यह प्रभावी रूप से उसी तरह है baz.call(undefined), इसलिए thisसंदर्भ को समाप्त करता है window। हम चाहते हैं bazपता चला है कि यह के अंतर्गत आता है obj, हम किसी भी तरह कि जानकारी जब प्रदान करने की आवश्यकता baz, कहा जाता है जो जहां के लिए पहला तर्क callया applyऔर संवृति भूमिका निभाते हैं।

स्कोप चेन

function bind(func, context)
{
    return function()
    {
        func.apply(context, arguments);
    };
}

जब कोई फ़ंक्शन निष्पादित किया जाता है, तो यह एक नया क्षेत्र बनाता है और इसमें किसी भी एन्कोडिंग स्कोप का संदर्भ होता है। जब अनाम फ़ंक्शन उपरोक्त उदाहरण में बनाया गया है, तो इसमें उस दायरे का संदर्भ है जो इसे बनाया गया था, जो कि bindगुंजाइश है। इसे "क्लोजर" के रूप में जाना जाता है।

[global scope (window)] - whereAmI, foo, obj, baz
    |
    [bind scope] - func, context
        |
        [anonymous scope]

जब आप किसी चर तक पहुँचने का प्रयास करते हैं तो इस "स्कोप चेन" को दिए गए नाम के साथ वैरिएबल खोजने के लिए चलाया जाता है - यदि वर्तमान स्कोप में वैरिएबल नहीं है, तो आप चेन में अगले स्कोप को देखते हैं, और इसी तरह जब तक आप नहीं पहुँचते। वैश्विक गुंजाइश। जब अनाम फ़ंक्शन वापस आ जाता है और bindनिष्पादित करना समाप्त हो जाता है, तो अनाम फ़ंक्शन में अभी भी bind's स्कोप' का संदर्भ है , इसलिए bindयह स्कोप "बहुत दूर" नहीं है।

उपरोक्त सभी को देखते हुए अब आपको यह समझने में सक्षम होना चाहिए कि निम्नलिखित उदाहरण में गुंजाइश कैसे काम करती है, और एक विशेष मूल्य के साथ "प्री-बाउंड" के आसपास एक फ़ंक्शन पास करने की तकनीक क्यों काम thisकरेगी जब इसे कहा जाता है:

>>> baz = bind(obj.foo, obj);
>>> baz(1, 2);
"this is obj with 2 arguments"

"जब किसी फ़ंक्शन का संदर्भ पास किया जाता है, तो इसके बारे में कोई अतिरिक्त जानकारी नहीं दी जाती है कि इसे कहाँ से ले जाया गया है" इसके लिए @insin को धन्यवाद दें।
12

9

क्या यह परिभाषित व्यवहार है? क्या यह क्रॉस-ब्राउज़र सुरक्षित है?

हाँ। और हाँ।

क्या कोई तर्क अंतर्निहित है कि यह ऐसा क्यों है ...

thisकटौती करने के लिए बहुत सरल अर्थ है:

  1. यदि thisएक निर्माण फ़ंक्शन के अंदर उपयोग किया जाता है, और फ़ंक्शन newकीवर्ड के साथ लागू किया गया था , तो thisउस ऑब्जेक्ट को संदर्भित करता है जिसे बनाया जाएगा। thisसार्वजनिक तरीकों से भी वस्तु का मतलब जारी रहेगा।
  2. अगर thisनेस्टेड प्रोटेक्टेड फंक्शन्स सहित अन्य कहीं भी उपयोग किया जाता है , तो यह ग्लोबल स्कोप (जो ब्राउज़र के मामले में विंडो ऑब्जेक्ट है) को संदर्भित करता है।

दूसरा मामला स्पष्ट रूप से एक डिज़ाइन दोष है, लेकिन क्लोजर का उपयोग करके इसके चारों ओर काम करना बहुत आसान है।


4

इस मामले में आंतरिक बाहरी फ़ंक्शन thisके thisचर के बजाय वैश्विक ऑब्जेक्ट के लिए बाध्य है । यह भाषा के डिजाइन का तरीका है।

एक अच्छी व्याख्या के लिए डगलस क्रॉकफोर्ड द्वारा "जावास्क्रिप्ट: द गुड पार्ट्स" देखें।


4

मुझे इस ECMAScript के बारे में एक अच्छा ट्यूटोरियल मिला

यह मान एक विशेष वस्तु है जो निष्पादन के संदर्भ से संबंधित है। इसलिए, इसे एक संदर्भ वस्तु के रूप में नामित किया जा सकता है (अर्थात एक वस्तु जिसके संदर्भ में निष्पादन संदर्भ सक्रिय है)।

संदर्भ के इस मूल्य के रूप में किसी भी वस्तु का उपयोग किया जा सकता है।

यह मान निष्पादन के संदर्भ की संपत्ति है, लेकिन परिवर्तनशील वस्तु की संपत्ति नहीं है।

यह सुविधा बहुत महत्वपूर्ण है, क्योंकि चर के विपरीत, यह मान कभी पहचानकर्ता संकल्प प्रक्रिया में भाग नहीं लेता है। यानी जब किसी कोड में इसे एक्सेस किया जाता है, तो इसका मूल्य सीधे निष्पादन के संदर्भ से लिया जाता है और बिना किसी स्कोप चेन लुकअप के। इस का मूल्य संदर्भ में प्रवेश करते समय केवल एक बार निर्धारित होता है।

वैश्विक संदर्भ में, यह मान स्वयं वैश्विक वस्तु है (इसका मतलब है, यह मान यहाँ चर वस्तु के बराबर है)

एक फ़ंक्शन संदर्भ के मामले में, हर एक फ़ंक्शन कॉल में यह मान भिन्न हो सकता है

संदर्भ जावास्क्रिप्ट-कोर और अध्याय-3-यह


" वैश्विक संदर्भ में, यह मान स्वयं वैश्विक ऑब्जेक्ट है (इसका मतलब है, यह मान यहाँ चर ऑब्जेक्ट के बराबर है) "। वैश्विक वस्तु के रूप में (ES4) "चर वस्तु" और ES5 पर्यावरण रिकॉर्ड है, वैश्विक निष्पादन संदर्भ का एक हिस्सा है। लेकिन वे वैश्विक वस्तु के लिए अलग-अलग संस्थाएं हैं (उदाहरण के लिए एक पर्यावरण रिकॉर्ड को सीधे संदर्भित नहीं किया जा सकता है, यह कल्पना द्वारा निषिद्ध है, लेकिन वैश्विक वस्तु हो सकती है)।
रॉबग
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.