मूल को संदर्भित करते हुए एक जावास्क्रिप्ट फ़ंक्शन को ओवरराइड करना


160

मेरे पास एक फ़ंक्शन है, a()जिसे मैं ओवरराइड करना चाहता हूं, लेकिन a()संदर्भ के आधार पर एक क्रम में मूल प्रदर्शन किया जाना चाहिए। उदाहरण के लिए, कभी-कभी जब मैं एक पेज बना रहा होता हूँ तो मैं इस तरह से ओवरराइड करना चाहता हूँ:

function a() {
    new_code();
    original_a();
}

और कभी-कभी इस तरह:

function a() {
    original_a();
    other_new_code();
}

original_a()ओवर-राइडिंग के भीतर से मुझे वह कैसे मिलेगा a()? क्या यह भी संभव है?

कृपया इस तरह से ओवर-राइडिंग के विकल्प का सुझाव न दें, मुझे बहुतों का पता है। मैं इस तरीके के बारे में विशेष रूप से पूछ रहा हूं।

जवाबों:


179

आप ऐसा कुछ कर सकते हैं:

var a = (function() {
    var original_a = a;

    if (condition) {
        return function() {
            new_code();
            original_a();
        }
    } else {
        return function() {
            original_a();
            other_new_code();
        }
    }
})();

original_aएक अनाम फ़ंक्शन के अंदर घोषित करना इसे वैश्विक नामस्थान को अव्यवस्थित करने से रोकता है, लेकिन यह आंतरिक कार्यों में उपलब्ध है।

टिप्पणियों में उल्लेख किए गए Nerdmaster की तरह, ()अंत में शामिल करना सुनिश्चित करें । आप बाहरी फ़ंक्शन को कॉल करना चाहते हैं और परिणाम (दो आंतरिक कार्यों में से एक) को aसंग्रहीत करते हैं, बाहरी फ़ंक्शन को स्वयं में संग्रहीत नहीं करते हैं a


25
अपने जैसे किसी भी बेवकूफ के लिए - अंत में "()" पर पूरा ध्यान दें - इसके बिना, यह बाहरी कार्य लौटाता है, न कि आंतरिक कार्य :)
Nerdmaster

@Nerdmaster कि बाहर इशारा करने के लिए धन्यवाद। मैंने लोगों को नोटिस करने में मदद करने के लिए एक नोट जोड़ा।
मैथ्यू क्रुमले

5
वाह, मैंने एक नेमस्पेस के बिना ऐसा करने की कोशिश की और मुझे स्टैक ओवरफ्लो मिला, लोल।
१:00:00

मुझे लगता है कि परिणाम पहले और अंतिम कोष्ठक समारोह से हटा दिए जाने पर समान होंगे। जैसे यहाँ से (functi..और })। बस करने }();से वही परिणाम प्राप्त होगा, जैसा (function{})()कि कोष्ठक के 1 सेट का उपयोग किया जाता है क्योंकि आप इसे घोषित करने के लिए केवल घोषणा, अनाम, फ़ंक्शन अभिव्यक्ति को कठबोली कर सकते हैं। लेकिन () करने से आप किसी फ़ंक्शन को वापस करने के लिए कुछ भी परिभाषित नहीं कर रहे हैं।
मुहम्मद उमर

@MuhammadUmer आप सही हैं कि फ़ंक्शन एक्सप्रेशन के आसपास कोष्ठक आवश्यक नहीं हैं। मैंने उन्हें शामिल किया क्योंकि उनके बिना, यदि आप पहली जोड़ी लाइनों को देखते हैं, तो ऐसा लगता है (कम से कम मेरे लिए) जैसे कि आप सीधे बाहरी फ़ंक्शन को असाइन कर रहे हैं a, इसे कॉल नहीं कर रहे हैं और रिटर्न मान संग्रहीत कर रहे हैं। कोष्ठक मुझे पता है कि वहाँ कुछ और चल रहा है।
मैथ्यू क्रुमले

88

प्रॉक्सी पैटर्न आप मदद कर सकते हैं:

(function() {
    // log all calls to setArray
    var proxied = jQuery.fn.setArray;
    jQuery.fn.setArray = function() {
        console.log( this, arguments );
        return proxied.apply( this, arguments );
    };
})();

उपरोक्त "प्रॉक्सी" को छिपाने के लिए एक फंक्शन में अपना कोड लपेटता है। यह jQuery के सेटअरे-मेथड को एक क्लोजर में सेव करता है और इसे ओवरराइट कर देता है। प्रॉक्सी तब सभी कॉल को विधि में लॉग करता है और कॉल को मूल में दर्शाता है। एप्लिकेशन (यह, तर्क) का उपयोग करना गारंटी देता है कि कॉलर मूल और अनुमानित विधि के बीच अंतर को नोटिस करने में सक्षम नहीं होगा।


10
वास्तव में। 'लागू' और 'तर्क' का उपयोग अन्य उत्तरों की तुलना में कहीं अधिक मजबूत बनाता है
रॉबर्ट लेवी

ध्यान दें कि इस पैटर्न को jQuery की आवश्यकता नहीं है - वे उदाहरण के रूप में jQuery के कार्यों में से एक का उपयोग कर रहे हैं।
केव

28

धन्यवाद दोस्तों प्रॉक्सी पैटर्न ने वास्तव में मदद की ..... वास्तव में मैं एक वैश्विक फ़ंक्शन फू को बुलाना चाहता था .. कुछ पृष्ठों में मुझे कुछ चेक करने की आवश्यकता है। इसलिए मैंने निम्नलिखित कार्य किया।

//Saving the original func
var org_foo = window.foo;

//Assigning proxy fucnc
window.foo = function(args){
    //Performing checks
    if(checkCondition(args)){
        //Calling original funcs
        org_foo(args);
    }
};

Thnx ने वास्तव में मेरी मदद की


3
प्रॉक्सी पैटर्न का पालन ​​करते समय , कुछ मामलों में इस विवरण को लागू करना महत्वपूर्ण होगा जो इस उत्तरदाता के कोड में मौजूद नहीं है: इसके बजाय org_foo(args), कॉल करें org_foo.call(this, args)। यह thisतब होता है जब यह विंडो होता है। जब सामान्य रूप से कॉल किया जाता है (अनुमानित नहीं)। इसका उत्तर
cellepo

10

आप किसी निर्माण का उपयोग करके किसी फ़ंक्शन को ओवरराइड कर सकते हैं:

function override(f, g) {
    return function() {
        return g(f);
    };
}

उदाहरण के लिए:

 a = override(a, function(original_a) {
      if (condition) { new_code(); original_a(); }
      else { original_a(); other_new_code(); }
 });

संपादित करें: एक टाइपो फिक्स्ड।


+1 यह अन्य प्रॉक्सी पैटर्न उदाहरणों की तुलना में बहुत अधिक पठनीय है!
मु माइंड

1
... लेकिन अगर मुझसे गलती नहीं हुई है, तो यह शून्य-तर्क कार्यों तक सीमित है, या कुछ पूर्वनिर्धारित संख्या में तर्क कम से कम। क्या पठनीयता को खोए बिना तर्कों की मनमानी संख्या को सामान्य करने का कोई तरीका है?
मु माइंड

@MuMind: हाँ, लेकिन प्रॉक्सी पैटर्न का ठीक से उपयोग करना - मेरा उत्तर देखें ।
डैन डस्केल्सस्कु

4

मनमानी तर्क पारित करना:

a = override(a, function(original_a) {
    if (condition) { new_code(); original_a.apply(this, arguments) ; }
    else { original_a.apply(this, arguments); other_new_code(); }
});

1
original_aहालांकि तर्क का उल्लेख नहीं होगा?
जोनाथन।

2

उत्तर जो @Matthew Crumley प्रदान करता है, वह तुरंत लागू किए गए फ़ंक्शन अभिव्यक्तियों का उपयोग कर रहा है, पुराने 'a' फ़ंक्शन को दिए गए फ़ंक्शन के निष्पादन संदर्भ में बंद करने के लिए। मुझे लगता है कि यह सबसे अच्छा जवाब था, लेकिन व्यक्तिगत रूप से, मैं IIFE के तर्क के रूप में फ़ंक्शन 'a' को पास करना पसंद करूंगा। मुझे लगता है कि यह अधिक समझ में आता है।

   var a = (function(original_a) {
        if (condition) {
            return function() {
                new_code();
                original_a();
            }
        } else {
            return function() {
                original_a();
                other_new_code();
            }
        }
    })(a);

1

ऊपर दिए गए उदाहरण सही ढंग से लागू नहीं होते हैं thisया argumentsफ़ंक्शन ओवरराइड के लिए सही तरीके से पास नहीं होते हैं। अंडरस्कोर _.wrap () मौजूदा फ़ंक्शन को लपेटता है, लागू होता है thisऔर argumentsसही तरीके से गुजरता है। देखें: http://underscorejs.org/#wrap


0

मेरे पास किसी और के द्वारा लिखे गए कुछ कोड थे और एक फ़ंक्शन में एक पंक्ति जोड़ना चाहता था जो मुझे कोड में नहीं मिल रहा था। इसलिए वर्कअराउंड के रूप में मैं इसे ओवरराइड करना चाहता था।

हालांकि मेरे लिए किसी भी समाधान ने काम नहीं किया।

यहाँ मेरे मामले में क्या काम किया गया है:

if (typeof originalFunction === "undefined") {
    originalFunction = targetFunction;
    targetFunction = function(x, y) {
        //Your code
        originalFunction(a, b);
        //Your Code
    };  
}

0

मैंने एक समान परिदृश्य के लिए एक छोटा सहायक बनाया है क्योंकि मुझे अक्सर कई पुस्तकालयों से कार्यों को ओवरराइड करने की आवश्यकता होती है। यह सहायक एक "नेमस्पेस" (फ़ंक्शन कंटेनर), फ़ंक्शन नाम और ओवरराइडिंग फ़ंक्शन को स्वीकार करता है। यह मूल फ़ंक्शन को निर्दिष्ट नामस्थान में नए के साथ बदल देगा।

नया फ़ंक्शन मूल फ़ंक्शन को पहले तर्क के रूप में स्वीकार करता है, और मूल फ़ंक्शन बाकी के रूप में तर्क देता है। यह संदर्भ को हर बार संरक्षित करेगा। यह शून्य और गैर-शून्य कार्यों का भी समर्थन करता है।

function overrideFunction(namespace, baseFuncName, func) {
    var originalFn = namespace[baseFuncName];
    namespace[baseFuncName] = function () {
        return func.apply(this, [originalFn.bind(this)].concat(Array.prototype.slice.call(arguments, 0)));
    };
}

उदाहरण के लिए बूटस्ट्रैप के साथ उपयोग:

overrideFunction($.fn.popover.Constructor.prototype, 'leave', function(baseFn, obj) {
    // ... do stuff before base call
    baseFn(obj);
    // ... do stuff after base call
});

हालांकि मैंने कोई प्रदर्शन परीक्षण नहीं बनाया। यह संभवतः कुछ अवांछित ओवरहेड जोड़ सकता है जो परिदृश्यों के आधार पर एक बड़ा सौदा हो सकता है या नहीं हो सकता है।


0

मेरी राय में शीर्ष उत्तर पठनीय / बनाए रखने योग्य नहीं हैं, और अन्य उत्तर संदर्भ को ठीक से बाँधते नहीं हैं। इन दोनों समस्याओं को हल करने के लिए ES6 सिंटैक्स का उपयोग करके यहां एक पठनीय समाधान दिया गया है।

const orginial = someObject.foo;
someObject.foo = function() {
  if (condition) orginial.bind(this)(...arguments);
};

लेकिन ES6 सिंटैक्स का उपयोग करने से यह उपयोग में प्रतिबंधित हो जाता है। क्या आपके पास एक संस्करण है जो ES5 में काम करता है?
ईच

0

तो मेरा जवाब एक समाधान है जो मुझे मूल वस्तु की ओर इशारा करते हुए _this चर का उपयोग करने की अनुमति देता है। मैं एक "स्क्वायर" का एक नया उदाहरण बनाता हूं, हालांकि मुझे जिस तरह से "स्क्वायर" उत्पन्न हुआ उससे आकार से नफरत है। मैंने सोचा कि इसे मेरी विशिष्ट आवश्यकताओं का पालन करना चाहिए। हालाँकि ऐसा करने के लिए मुझे उस फ़ंक्शन के इंटर्नल के साथ एक अपडेटेड "गेटसाइज़" फंक्शन के लिए स्क्वायर की आवश्यकता थी, जो इस तरह के पहले से मौजूद अन्य फंक्शन्स को कॉल कर रहा है, जैसे कि, यह ।GetVolume ()। लेकिन ऐसा करने के लिए मुझे बिना किसी क्रेजी हैक्स के ऐसा करने की जरूरत थी। तो यहाँ मेरा समाधान है।

कुछ अन्य ऑब्जेक्ट इनिशलाइज़र या हेल्पर फ़ंक्शन।

this.viewer = new Autodesk.Viewing.Private.GuiViewer3D(
  this.viewerContainer)
var viewer = this.viewer;
viewer.updateToolbarButtons =  this.updateToolbarButtons(viewer);

अन्य वस्तु में कार्य।

updateToolbarButtons = function(viewer) {
  var _viewer = viewer;
  return function(width, height){ 
blah blah black sheep I can refer to this.anything();
}
};

0

सुनिश्चित नहीं है कि यह सभी परिस्थितियों में काम करेगा, लेकिन हमारे मामले में, हम describeजेस्ट में फ़ंक्शन को ओवरराइड करने की कोशिश कर रहे थे ताकि हम नाम को पार्स कर सकें और पूरे को छोड़ सकेंdescribe कुछ मानदंडों ब्लॉक को ।

यहाँ हमारे लिए क्या काम किया गया है:

function describe( name, callback ) {
  if ( name.includes( "skip" ) )
    return this.describe.skip( name, callback );
  else
    return this.describe( name, callback );
}

यहां दो चीजें महत्वपूर्ण हैं:

  1. हम एक तीर फ़ंक्शन का उपयोग नहीं करते हैं () =>

    एरो फ़ंक्शंस संदर्भ को बदलते हैं thisऔर हमें फ़ाइल की आवश्यकता होती है this

  2. के उपयोग this.describeऔर this.describe.skipके बजाय सिर्फ describeऔर describe.skip

फिर, यह निश्चित नहीं है कि यह किसी के लिए भी महत्वपूर्ण है, लेकिन हमने मूल रूप से मैथ्यू क्रुमले के उत्कृष्ट उत्तर के साथ दूर होने की कोशिश की, लेकिन हमारी विधि को एक समारोह बनाने और उन्हें सशर्त में पार्स करने के लिए परम स्वीकार करने की आवश्यकता थी।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.