2009-06-17 19 views
76

मैं केवल this question पढ़ रहा था और फ़ंक्शन-रैपर विधि के बजाय उपनाम विधि को आजमा देना चाहता था, लेकिन मुझे फ़ायरफ़ॉक्स 3 या 3.5beta4 में काम करने के लिए प्रतीत नहीं होता , या Google क्रोम, दोनों अपनी डीबग विंडो और एक परीक्षण वेब पेज में।जावास्क्रिप्ट फ़ंक्शन एलाइजिंग काम नहीं कर रहा है

Firebug:

>>> window.myAlias = document.getElementById 
function() 
>>> myAlias('item1') 
>>> window.myAlias('item1') 
>>> document.getElementById('item1') 
<div id="item1"> 

अगर मैं एक वेब पेज में डाल दिया, myAlias ​​करने के लिए कॉल मुझे इस त्रुटि देता है:

uncaught exception: [Exception... "Illegal operation on WrappedNative prototype object" nsresult: "0x8057000c (NS_ERROR_XPC_BAD_OP_ON_WN_PROTO)" location: "JS frame :: file:///[...snip...]/test.html :: <TOP_LEVEL> :: line 7" data: no] 

क्रोम (>>> के साथ स्पष्टता के लिए डाला) :

>>> window.myAlias = document.getElementById 
function getElementById() { [native code] } 
>>> window.myAlias('item1') 
TypeError: Illegal invocation 
>>> document.getElementById('item1') 
<div id=?"item1">? 

और परीक्षण पृष्ठ में, मुझे वही "अवैध आमंत्रण" मिलता है।

क्या मैं कुछ गलत कर रहा हूं? क्या कोई और इसे पुन: उत्पन्न कर सकता है?

इसके अलावा, विचित्र रूप से पर्याप्त, मैंने अभी कोशिश की और यह आईई 8 में काम करता है।

+0

IE8 में - यह जादू समारोह या कुछ और किसी तरह का हो सकता है। –

+0

मैंने http://stackoverflow.com/questions/954417 पर गलत उत्तरों पर टिप्पणियां जोड़ दी हैं और उन्हें यहां निर्देशित किया है। –

+1

फ़ंक्शन.प्रोटोटाइप.बिंड विधि का समर्थन करने वाले ब्राउज़र के लिए: window.myAlias ​​= document.getElementById.bind (दस्तावेज़); एमडीएन दस्तावेज़ खोजें, एक बाध्य शिम होगा। –

उत्तर

33

आपको दस्तावेज़ ऑब्जेक्ट में उस विधि को बांधना होगा। देखो:

>>> $ = document.getElementById 
getElementById() 
>>> $('bn_home') 
[Exception... "Cannot modify properties of a WrappedNative" ... anonymous :: line 72 data: no] 
>>> $.call(document, 'bn_home') 
<body id="bn_home" onload="init();"> 

जब आप एक सरल उर्फ ​​कर रहे हैं, समारोह वैश्विक वस्तु पर कहा जाता है, दस्तावेज़ वस्तु पर नहीं। इसे ठीक करने के लिए बंद करने वाली तकनीक का उपयोग करें:

function makeAlias(object, name) { 
    var fn = object ? object[name] : null; 
    if (typeof fn == 'undefined') return function() {} 
    return function() { 
     return fn.apply(object, arguments) 
    } 
} 
$ = makeAlias(document, 'getElementById'); 

>>> $('bn_home') 
<body id="bn_home" onload="init();"> 

इस तरह आप मूल वस्तु का संदर्भ नहीं खोते हैं।

2012 में, वहाँ है कि हम एक सजावटी तरह से यह करने के लिए अनुमति देता है ES5 से नए bind विधि है:

>>> $ = document.getElementById.bind(document) 
>>> $('bn_home') 
<body id="bn_home" onload="init();"> 
+3

जो वास्तव में एक रैपर है क्योंकि यह एक नया कार्य देता है। मुझे लगता है कि केवी के सवाल का जवाब यह है कि ऊपर वर्णित कारणों के लिए यह संभव नहीं है। जिस विधि का आप यहां वर्णन करते हैं वह शायद सबसे अच्छा विकल्प है। – jiggy

+0

आपकी टिप्पणी के लिए धन्यवाद, मैंने इसे समझने के लिए पर्याप्त रूप से पर्याप्त नहीं देखा था। तो, दूसरे प्रश्न पर उत्तर में वाक्यविन्यास पूरी तरह से फर्जी है? दिलचस्प ... – Kev

+0

असल में, आपको इसे उत्तर के रूप में सबमिट करना चाहिए ... – Kev

-6

आप वास्तव में नहीं कर सकते हैं "शुद्ध उर्फ" एक पूर्वनिर्धारित वस्तु पर एक समारोह । इसलिए, सबसे करीब एक ही वस्तु के भीतर रह कर आप रैपिंग के बिना प्राप्त कर सकते हैं aliasing करने के लिए किया जाता है:

>>> document.s = document.getElementById; 
>>> document.s('myid'); 
<div id="myid"> 
+5

यह थोड़ा गलत है। आप 'शुद्ध उपनाम' एक फ़ंक्शन प्रदान कर सकते हैं बशर्ते फ़ंक्शन कार्यान्वयन का 'इस' का उपयोग करें। तो यह निर्भर करता है। – SolutionYogi

+0

क्षमा करें, मेरा मतलब getElementById के संदर्भ में था। आप सही हे। मैंने इसे प्रतिबिंबित करने के लिए थोड़ा जवाब बदल दिया है ... – Kev

+0

ओह केवी यह वास्तव में ओपी lolzzzz –

181

मैं इस विशेष व्यवहार को समझने के गहरे खोदा और मुझे लगता है कि मैं एक अच्छा स्पष्टीकरण मिल गया है।

इससे पहले कि मैं document.getElementById को उपनाम करने में सक्षम न हो, मैं यह बताने की कोशिश करूंगा कि जावास्क्रिप्ट फ़ंक्शन/ऑब्जेक्ट्स कैसे काम करते हैं।

जब भी आप जावास्क्रिप्ट फ़ंक्शन का आह्वान करते हैं, तो जावास्क्रिप्ट दुभाषिया एक दायरा निर्धारित करता है और इसे फ़ंक्शन में पास करता है।

समारोह निम्नलिखित पर विचार करें:

function sum(a, b) 
{ 
    return a + b; 
} 

sum(10, 20); // returns 30; 

इस समारोह विंडो दायरे में घोषित किया जाता है और जब आप इसे योग समारोह के अंदर this का मूल्य आह्वान वैश्विक Window वस्तु हो जाएगा।

'योग' फ़ंक्शन के लिए, इससे कोई फर्क नहीं पड़ता कि 'इस' का मूल्य क्या है क्योंकि यह इसका उपयोग नहीं कर रहा है।

function Person(birthDate) 
{ 
    this.birthDate = birthDate;  
    this.getAge = function() { return new Date().getFullYear() - this.birthDate.getFullYear(); }; 
} 

var dave = new Person(new Date(1909, 1, 1)); 
dave.getAge(); //returns 100. 

जब आप dave.getAge फ़ंक्शन को कॉल करें, जावास्क्रिप्ट दुभाषिया देखता है कि आप, dave वस्तु पर getAge समारोह बुला रहे हैं तो यह this को dave सेट करता है और कहता है:


समारोह निम्नलिखित पर विचार करें getAge फ़ंक्शन। getAge() सही ढंग से 100 लौटाएगा।


तुम्हें पता लग सकता है कि जावास्क्रिप्ट में आप apply पद्धति का उपयोग करके गुंजाइश निर्दिष्ट कर सकते हैं। आइए कोशिश करें।

var dave = new Person(new Date(1909, 1, 1)); //Age 100 in 2009 
var bob = new Person(new Date(1809, 1, 1)); //Age 200 in 2009 

dave.getAge.apply(bob); //returns 200. 

ऊपर लाइन में, बजाय दे जावास्क्रिप्ट गुंजाइश तय की है, तो आप गुंजाइश मैन्युअल bob वस्तु के रूप में गुजर रहे हैं। getAge अब 200 लौटाएगा, भले ही आपने 'सोचा' getAge को dave ऑब्जेक्ट पर कॉल किया हो।


उपर्युक्त सभी का क्या मतलब है? फ़ंक्शन आपके जावास्क्रिप्ट ऑब्जेक्ट्स से 'ढीले' जुड़े हुए हैं। जैसे आप

var dave = new Person(new Date(1909, 1, 1)); 
var bob = new Person(new Date(1809, 1, 1)); 

bob.getAge = function() { return -1; }; 

bob.getAge(); //returns -1 
dave.getAge(); //returns 100 

का अगला कदम लेते हैं कर सकते हैं।

var dave = new Person(new Date(1909, 1, 1)); 
var ageMethod = dave.getAge; 

dave.getAge(); //returns 100; 
ageMethod(); //returns ????? 

ageMethod निष्पादन एक त्रुटि फेंकता है! क्या हुआ?

तुम मेरे ऊपर अंक को ध्यान से पढ़ें, तो आप ध्यान दें होता है कि dave.getAge विधि davethis के रूप में वस्तु के साथ बुलाया गया था जबकि जावास्क्रिप्ट ageMethod निष्पादन के लिए 'विषय-क्षेत्र' निर्धारित नहीं कर सका। तो यह वैश्विक 'विंडो' को 'इस' के रूप में पारित कर दिया। अब window में birthDate संपत्ति नहीं है, ageMethod निष्पादन विफल हो जाएगा।

इसे कैसे ठीक करें? सरल,

ageMethod.apply(dave); //returns 100. 

ऊपर मेकअप भावना के सभी था? यदि ऐसा है, तो आप क्यों तुम उर्फ ​​document.getElementById में सक्षम नहीं हैं व्याख्या करने में सक्षम हो जाएगा:

var $ = document.getElementById; 

$('someElement'); 

$windowthis के रूप में के साथ कहा जाता है और अगर getElementById कार्यान्वयन this उम्मीद कर रही है document हो सकता है, यह असफल हो जायेगी।

फिर इसे ठीक करने के लिए, आप कर सकते हैं

$.apply(document, ['someElement']); 

तो क्यों यह इंटरनेट एक्सप्लोरर में काम करता है?

मुझे आईई में getElementById के आंतरिक कार्यान्वयन को नहीं पता है, लेकिन jQuery स्रोत (inArray विधि कार्यान्वयन) में एक टिप्पणी का कहना है कि आईई, window == document में।यदि ऐसा है, तो document.getElementById को अलियासिंग करना IE में काम करना चाहिए।

इसे आगे बढ़ाने के लिए, मैंने एक विस्तृत उदाहरण बनाया है। नीचे Person फ़ंक्शन पर एक नज़र डालें।

function Person(birthDate) 
{ 
    var self = this; 

    this.birthDate = birthDate; 

    this.getAge = function() 
    { 
     //Let's make sure that getAge method was invoked 
     //with an object which was constructed from our Person function. 
     if(this.constructor == Person) 
      return new Date().getFullYear() - this.birthDate.getFullYear(); 
     else 
      return -1; 
    }; 

    //Smarter version of getAge function, it will always refer to the object 
    //it was created with. 
    this.getAgeSmarter = function() 
    { 
     return self.getAge(); 
    }; 

    //Smartest version of getAge function. 
    //It will try to use the most appropriate scope. 
    this.getAgeSmartest = function() 
    { 
     var scope = this.constructor == Person ? this : self; 
     return scope.getAge(); 
    }; 

} 

ऊपर Person समारोह के लिए, यहाँ कैसे विभिन्न getAge तरीकों व्यवहार करेंगे है।

चलो Person फ़ंक्शन का उपयोग करके दो ऑब्जेक्ट बनाएं।

var yogi = new Person(new Date(1909, 1,1)); //Age is 100 
var anotherYogi = new Person(new Date(1809, 1, 1)); //Age is 200 

console.log(yogi.getAge()); //Output: 100. 

सीधे आगे, getAge विधि this और आउटपुट 100 रूप yogi वस्तु हो जाता है।


var ageAlias = yogi.getAge; 
console.log(ageAlias()); //Output: -1 

जावास्क्रिप्ट interepreter this रूप window वस्तु सेट करता है और हमारे getAge विधि -1 वापस आ जाएगी।


console.log(ageAlias.apply(yogi)); //Output: 100 

अगर हम सही गुंजाइश निर्धारित करते हैं, आप ageAlias विधि का उपयोग कर सकते हैं।


console.log(ageAlias.apply(anotherYogi)); //Output: 200 

अगर हम किसी अन्य व्यक्ति वस्तु में पारित, यह अभी भी उम्र सही ढंग से की गणना करेगा।

var ageSmarterAlias = yogi.getAgeSmarter;  
console.log(ageSmarterAlias()); //Output: 100 

ageSmarter समारोह मूल this वस्तु पर कब्जा कर लिया अब तो आप सही गुंजाइश की आपूर्ति के बारे में चिंता करने की जरूरत नहीं है।


console.log(ageSmarterAlias.apply(anotherYogi)); //Output: 100 !!! 

ageSmarter के साथ समस्या यह है कि आप किसी अन्य वस्तु के लिए गुंजाइश सेट नहीं कर सकता है।


var ageSmartestAlias = yogi.getAgeSmartest; 
console.log(ageSmartestAlias()); //Output: 100 
console.log(ageSmartestAlias.apply(document)); //Output: 100 

ageSmartest समारोह मूल गुंजाइश है, तो गलत गुंजाइश आपूर्ति की है का उपयोग करेगा।


console.log(ageSmartestAlias.apply(anotherYogi)); //Output: 200 

तुम अब भी getAgeSmartest के लिए एक और Person वस्तु पारित करने के लिए सक्षम हो जाएगा। :)

+7

+1 क्यों आईई काम करता है। बाकी थोड़ा सा विषय है लेकिन सामान्य रूप से अभी भी सहायक है।:) – Kev

+42

उत्कृष्ट जवाब। मैं नहीं देखता कि इसमें से कोई भी विषय से कैसे दूर है। –

+0

वास्तव में बहुत अच्छा जवाब है। कुछ हद तक छोटा संस्करण लिखा गया है [यहां] (http://stackoverflow.com/questions/585840/implement-map-in-javascript-that-supports-object-methods-as-mapped-functions/585918#585918)। –

3

यह एक छोटा सा जवाब है।

निम्नलिखित फ़ंक्शन का एक प्रति (संदर्भ) बनाता है। समस्या यह है कि अब window ऑब्जेक्ट पर फ़ंक्शन document ऑब्जेक्ट पर रहने के लिए डिज़ाइन किया गया था।

window.myAlias = document.getElementById 

विकल्प

  • एक आवरण (पहले से ही फेबियन MENAGER ने उल्लेख किया)
  • उपयोग करने के लिए या तो आपके पास दो उपनाम का उपयोग कर सकते हैं।

    window.d = document // A renamed reference to the object 
    window.d.myAlias = window.d.getElementById 
    
2

एक और संक्षिप्त उत्तर है, बस रैपिंग के लिए/console.log और इसी तरह के प्रवेश तरीकों aliasing। वे सभी console संदर्भ में होने की उम्मीद करते हैं।

कुछ फॉलबैक के साथ console.log को लपेटते समय यह उपयोग योग्य है, यदि आपके या आपके उपयोगकर्ता using a browser that doesn't (always) support it पर परेशानी में हैं। यह उस समस्या का पूरा समाधान नहीं है, हालांकि इसे विस्तारित चेक और फॉलबैक की आवश्यकता है - आपका माइलेज भिन्न हो सकता है।

उदाहरण का उपयोग करते हुए चेतावनी

var warn = function(){ console.warn.apply(console, arguments); } 

तो फिर आप अपने प्रवेश तर्क एक सरणी में लिपटे देखना पसंद करते हैं के रूप में हमेशा की तरह

warn("I need to debug a number and an object", 9999, { "user" : "Joel" }); 

इसका इस्तेमाल (मैं करता हूँ, ज्यादातर समय), स्थानापन्न .apply(...).call(...) के साथ।

console.log(), console.debug(), console.info(), console.warn(), console.error() साथ काम करना चाहिए। console on MDN भी देखें।

1

अन्य महान जवाब के अलावा, वहाँ सरल jQuery विधि $.proxy है।

आप इस तरह उर्फ ​​कर सकते हैं:

myAlias = $.proxy(document, 'getElementById'); 

या

myAlias = $.proxy(document.getElementById, document); 
+0

+1 पढ़ने के बाद सॉफ़्टवेयर इंजीनियरिंग का शासन किया गया है क्योंकि यह एक व्यवहार्य समाधान है। लेकिन, दृढ़ता से बोलते हुए, दृश्यों के पीछे '$ .proxy' वास्तव में एक रैपर भी है। – pimvdb

संबंधित मुद्दे