जावास्क्रिप्ट में फ़ंक्शन जिसे केवल एक बार कहा जा सकता है


116

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


~ 8 साल बाद मैंने अपने डेकोरेटर्स लिब ( github.com/vlio20/utils-decorators ) के लिए एक डेकोरेटर बनाने के लिए एक फ़ीचर रिक्वेस्ट बनाई, जो बिल्कुल ऐसा ही करेगा ( github.com/vlio20/utils-decorators/issues/77 )
vlio20

जवाबों:


221

यदि "द्वारा निष्पादित नहीं किया जाएगा" आप का मतलब है "जब एक से अधिक बार कॉल किया जाता है तो कुछ भी नहीं होगा", आप एक क्लोजर बना सकते हैं:

var something = (function() {
    var executed = false;
    return function() {
        if (!executed) {
            executed = true;
            // do something
        }
    };
})();

something(); // "do something" happens
something(); // nothing happens

@Vladloffe (अब हटाए गए) द्वारा एक टिप्पणी के जवाब में: एक वैश्विक चर के साथ, अन्य कोड "निष्पादित" ध्वज (जो भी नाम आप इसके लिए चुनते हैं) के मूल्य को रीसेट कर सकते हैं। बंद होने के साथ, अन्य कोड के पास ऐसा करने का कोई तरीका नहीं है, या तो गलती से या जानबूझकर।

जैसा कि अन्य उत्तर यहां बताते हैं, कई पुस्तकालयों (जैसे कि अंडरस्कोर और रामदा ) में एक छोटी उपयोगिता फ़ंक्शन है (आमतौर पर नाम once()[*] ) है जो एक फ़ंक्शन को एक तर्क के रूप में स्वीकार करता है और एक और फ़ंक्शन देता है जो आपूर्ति फ़ंक्शन को बिल्कुल एक बार कॉल करता है, भले ही कैसे भी हो। कई बार दिए गए फंक्शन को कहा जाता है। लौटाया गया फ़ंक्शन पहले दिए गए फ़ंक्शन द्वारा लौटाए गए मूल्य को भी कैश करता है और बाद की कॉल पर वापस करता है।

हालाँकि, यदि आप इस तरह की थर्ड-पार्टी लाइब्रेरी का उपयोग नहीं कर रहे हैं, लेकिन फिर भी ऐसा यूटिलिटी फंक्शन चाहते हैं (न कि ऊपर दिए गए नॉन सॉल्यूशन के बजाय), इसे लागू करना काफी आसान है। मैंने जो सबसे अच्छा संस्करण देखा है, वह डेविड वॉल्श द्वारा पोस्ट किया गया है :

function once(fn, context) { 
    var result;
    return function() { 
        if (fn) {
            result = fn.apply(context || this, arguments);
            fn = null;
        }
        return result;
    };
}

मैं बदलने के लिए इच्छुक हो जाएगा fn = null;करने के लिए fn = context = null;। एक contextबार fnबुलाए जाने के संदर्भ को बनाए रखने के लिए बंद करने का कोई कारण नहीं है ।

[*] जागरूक रहें, हालांकि, अन्य पुस्तकालयों, जैसे कि jQuery के इस Drupal एक्सटेंशन के नाम पर एक फ़ंक्शन हो सकता है, once()जो कुछ अलग करता है।


1
बहुत अच्छा काम करता है, क्या आप इसके पीछे लॉगिन की व्याख्या कर सकते हैं, कैसे var निष्पादित किया गया = गलत; काम करता है
Amr.Ayoub

@EgyCode - यह बंद होने पर MDN प्रलेखन में अच्छी तरह से समझाया गया है
टेड होप

क्षमा करें, मेरा मतलब तर्क था, मैं बूलियन
संस्करण

1
@EgyCode - कुछ संदर्भों में (जैसे ifकथन परीक्षण अभिव्यक्ति में), जावास्क्रिप्ट को एक मान की उम्मीद है trueया falseजब अभिव्यक्ति का मूल्यांकन किया जाता है, तो प्रोग्राम प्रवाह मान के अनुसार प्रतिक्रिया करता है। सशर्त संचालक ==हमेशा बूलियन मान का मूल्यांकन करते हैं। एक चर trueया तो पकड़ सकता है या false। (अधिक जानकारी के लिए, पर दस्तावेज़ीकरण देखें बूलियन , truthy , और falsey ।)
टेड हॉप

मुझे समझ में नहीं आता है कि हर नए कॉल के साथ निष्पादन क्यों नहीं किया जाता है। क्या कोई इसे समझा सकता है, कृपया
जुआनस कोरा

64

इसे पुन: प्रयोज्य NOOP (कोई ऑपरेशन) फ़ंक्शन के साथ बदलें ।

// this function does nothing
function noop() {};

function foo() {
    foo = noop; // swap the functions

    // do your thing
}

function bar() {
    bar = noop; // swap the functions

    // do your thing
}

11
@fableal: यह कैसे अटपटा है? फिर से, यह बहुत साफ है, कम कोड की आवश्यकता होती है, और हर फ़ंक्शन के लिए एक नया चर की आवश्यकता नहीं होती है जिसे अक्षम किया जाना चाहिए। इस तरह की स्थिति के लिए एक "नोप" बिल्कुल डिज़ाइन किया गया है।
हेट लज़ीज़

1
@fableal: मैंने सिर्फ हकरा के जवाब को देखा। इसलिए हर बार जब आप एक नए समारोह में ऐसा करने के लिए एक नया बंद और चर बनाते हैं? आपके पास "सुरुचिपूर्ण" की बहुत मज़ेदार परिभाषा है।
मैं हेट लज़ीज़

2
Asawyer की प्रतिक्रिया के अनुसार, आपको केवल _.once (foo) या _.once (बार) करने की आवश्यकता है, और फ़ंक्शंस को स्वयं केवल एक बार चलाने के लिए जागरूक होने की आवश्यकता नहीं है (noop की कोई आवश्यकता नहीं है और इसके लिए कोई ज़रूरत नहीं है) * = नूप)।
fableal

7
वास्तव में सबसे अच्छा समाधान नहीं है। यदि आप इस फ़ंक्शन को कॉलबैक के रूप में पास कर रहे हैं, तो इसे अभी भी कई बार कहा जा सकता है। उदाहरण के लिए: setInterval(foo, 1000)- और पहले से ही यह काम नहीं करता है। आप वर्तमान दायरे में संदर्भ को अधिलेखित कर रहे हैं।
एक बिल्ली

1
पुन: प्रयोज्य invalidateफ़ंक्शन जो setIntervalआदि के साथ काम करता है ।: jsbin.com/vicipar/1/edit?js,console
Q20

32

एक खाली समारोह की ओर इशारा करते हुए कहा जाता है:

function myFunc(){
     myFunc = function(){}; // kill it as soon as it was called
     console.log('call once and never again!'); // your stuff here
};
<button onClick=myFunc()>Call myFunc()</button>


या, जैसे:

var myFunc = function func(){
     if( myFunc.fired ) return;
     myFunc.fired = true;
     console.log('called once and never again!'); // your stuff here
};

// even if referenced & "renamed"
((refToMyfunc)=>{
  setInterval(refToMyfunc, 1000);
})(myFunc)


1
यह समाधान जावास्क्रिप्ट जैसी अत्यधिक गतिशील भाषा की भावना में बहुत अधिक है। सेमाफोर क्यों सेट करें, जब आप एक बार इसका उपयोग करने के बाद फ़ंक्शन को खाली कर सकते हैं?
इवान Ivanurdinjaković

बहुत अच्छा समाधान! यह समाधान क्लोजर दृष्टिकोण से भी बेहतर प्रदर्शन कर रहा है। एकमात्र मामूली "दोष" यह है कि आपको नाम बदलने पर फ़ंक्शन नाम को सिंक में रखने की आवश्यकता है।
लियोनेल

5
इसके साथ समस्या यह है कि यदि कहीं फ़ंक्शन का एक और संदर्भ है (जैसे कि इसे एक तर्क के रूप में पारित किया गया था और कहीं और एक चर में टकराया था - जैसा कि एक कॉल में setInterval()) तो संदर्भ मूल कार्यक्षमता को दोहराएगा जब कहा जाता है।
टेड होप

@ टेडहॉप - यहाँ उन मामलों के लिए एक विशेष उपचार है
vsync

1
हां, यह बिल्कुल इस धागे पर बनीक के जवाब जैसा है । यह एक क्लोजर के समान है (जैसा कि मेरे उत्तर में है ) लेकिन एक क्लोजर चर के बजाय एक संपत्ति का उपयोग करना। इस मामले में दोनों मामले आपके दृष्टिकोण से काफी अलग हैं।
टेड होप

25

UnderscoreJs का एक कार्य है जो कि, underscorejs.org/#once करता है

  // Returns a function that will be executed at most one time, no matter how
  // often you call it. Useful for lazy initialization.
  _.once = function(func) {
    var ran = false, memo;
    return function() {
      if (ran) return memo;
      ran = true;
      memo = func.apply(this, arguments);
      func = null;
      return memo;
    };
  };

1
मुझे onceतर्क स्वीकार करना अजीब लगता है। आप कर सकते हैं squareo = _.once(square); console.log(squareo(1)); console.log(squareo(2));और 1दोनों कॉल के लिए प्राप्त कर सकते हैं squareo। क्या मुझे यह सही समझ आ रहा है?
aschmied

@aschmied आप सही हैं - पहली कॉल के तर्कों के परिणाम को याद किया जाएगा और पैरामीटर की परवाह किए बिना सभी अन्य कॉल के लिए वापस लौटा दिया जाएगा क्योंकि अंतर्निहित फ़ंक्शन को फिर से कभी नहीं बुलाया जाता है। इस तरह के मामलों में मैं _.onceविधि का उपयोग करने का सुझाव नहीं देता । देखें jsfiddle.net/631tgc5f/1
asawyer

1
@aschmied या मुझे लगता है कि प्रति तर्क सेट के लिए एक बार एक अलग कॉल का उपयोग करें। मुझे नहीं लगता कि यह वास्तव में उस तरह के उपयोग के लिए अभिप्रेत है।
asawyer

1
काम अगर आप पहले से ही उपयोग कर रहे हैं _; मैं इस तरह के एक छोटे से कोड के लिए पूरे पुस्तकालय पर निर्भर नहीं होता।
जो शहनहा

11

स्टैटिक वैरिएबल की बात करें तो यह क्लोजर वैरिएंट जैसा है।

var once = function() {
    if(once.done) return;
    console.log('Doing this once!');
    once.done = true;
};

once(); once(); 

यदि आप चाहें तो आप एक समारोह रीसेट कर सकते हैं:

once.done = false;


3

आप बस समारोह "खुद को हटा सकते हैं"

function Once(){
    console.log("run");

    Once = undefined;
}

Once();  // run
Once();  // Uncaught TypeError: undefined is not a function 

लेकिन यह सबसे अच्छा जवाब नहीं हो सकता है अगर आप त्रुटियों को निगल नहीं करना चाहते हैं।

आप यह भी कर सकते हैं:

function Once(){
    console.log("run");

    Once = function(){};
}

Once(); // run
Once(); // nothing happens

मुझे इसे स्मार्ट पॉइंटर की तरह काम करने की आवश्यकता है, अगर ए से कोई तत्व नहीं है तो इसे निष्पादित किया जा सकता है, अगर एक या एक से अधिक ए तत्वों को निष्पादित नहीं किया जा सकता है।

function Conditional(){
    if (!<no elements from type A>) return;

    // do stuff
}

1
मुझे इसे स्मार्ट पॉइंटर की तरह काम करने की आवश्यकता है, अगर ए से कोई तत्व नहीं है तो इसे निष्पादित किया जा सकता है, अगर एक या एक से अधिक ए तत्वों को निष्पादित नहीं किया जा सकता है।
vlio20

@VladIoffe वह नहीं है जो आपने पूछा था।
शमिड्टी

यह काम नहीं करेगा यदि Onceकॉल-बैक (जैसे setInterval(Once, 100)) के रूप में पास किया गया हो। मूल कार्य कहा जाता रहेगा।
टेड हॉप

2

इसे इस्तेमाल करे

var fun = (function() {
  var called = false;
  return function() {
    if (!called) {
      console.log("I  called");
      called = true;
    }
  }
})()

2

क्रॉकफोर्ड नाम के कुछ दोस्त से ... :)

function once(func) {
    return function () {
        var f = func;
        func = null;
        return f.apply(
            this,
            arguments
        );
    };
}

1
यह महान है अगर आपको लगता है कि TypeError: Cannot read property 'apply' of nullमहान है। दूसरी बार जब आप लौटाए गए फ़ंक्शन को आमंत्रित करते हैं।
टेड हॉप

2

पुन: प्रयोज्य invalidateसमारोह जो साथ काम करता है setInterval:

var myFunc = function (){
  if (invalidate(arguments)) return;
  console.log('called once and never again!'); // your stuff here
};

const invalidate = function(a) {
  var fired = a.callee.fired;
  a.callee.fired = true;
  return fired;
}

setInterval(myFunc, 1000);

इसे JSBin पर आज़माएं: https://jsbin.com/vicipar/edit?js,console

की भिन्नता Bunyk से जवाब


1

यहाँ एक उदाहरण है JSFiddle - http://jsfiddle.net/6yL6t/

और कोड:

function hashCode(str) {
    var hash = 0, i, chr, len;
    if (str.length == 0) return hash;
    for (i = 0, len = str.length; i < len; i++) {
        chr   = str.charCodeAt(i);
        hash  = ((hash << 5) - hash) + chr;
        hash |= 0; // Convert to 32bit integer
    }
    return hash;
}

var onceHashes = {};

function once(func) {
    var unique = hashCode(func.toString().match(/function[^{]+\{([\s\S]*)\}$/)[1]);

    if (!onceHashes[unique]) {
        onceHashes[unique] = true;
        func();
    }
}

तुम यह कर सकते थे:

for (var i=0; i<10; i++) {
    once(function() {
        alert(i);
    });
}

और यह केवल एक बार चलेगा :)


1

प्रारंभिक व्यवस्था:

var once = function( once_fn ) {
    var ret, is_called;
    // return new function which is our control function 
    // to make sure once_fn is only called once:
    return function(arg1, arg2, arg3) {
        if ( is_called ) return ret;
        is_called = true;
        // return the result from once_fn and store to so we can return it multiply times:
        // you might wanna look at Function.prototype.apply:
        ret = once_fn(arg1, arg2, arg3);
        return ret;
    };
}

1

यदि आपका Node.js का उपयोग कर रहा है या ब्राउज़र के साथ जावास्क्रिप्ट लिख रहा है, तो "एक बार" npm मॉड्यूल पर विचार करें :

var once = require('once')

function load (file, cb) {
  cb = once(cb)
  loader.load('file')
  loader.once('load', cb)
  loader.once('error', cb)
}

1

यदि आप भविष्य में फ़ंक्शन का पुन: उपयोग करने में सक्षम होना चाहते हैं तो यह ऊपर दिए गए एड हॉप के कोड के आधार पर अच्छी तरह से काम करता है (मुझे पता है कि मूल प्रश्न इस अतिरिक्त सुविधा के लिए कॉल नहीं किया है!):

   var something = (function() {
   var executed = false;              
    return function(value) {
        // if an argument is not present then
        if(arguments.length == 0) {               
            if (!executed) {
            executed = true;
            //Do stuff here only once unless reset
            console.log("Hello World!");
            }
            else return;

        } else {
            // otherwise allow the function to fire again
            executed = value;
            return;
        }       
    }
})();

something();//Hello World!
something();
something();
console.log("Reset"); //Reset
something(false);
something();//Hello World!
something();
something();

आउटपुट जैसा दिखता है:

Hello World!
Reset
Hello World!

1

जब आप की जरूरत है कि लिखने के लिए आसान है कि साधारण डेकोरेटर

function one(func) {
  return function () {
     func && func.apply(this, arguments);
     func = null;
  }
}

का उपयोग करते हुए:

var initializer= one( _ =>{
      console.log('initializing')
  })

initializer() // 'initializing'
initializer() // nop
initializer() // nop

0

अंडरस्कोर "एक बार" फ़ंक्शन का उपयोग करने की कोशिश कर रहा है:

var initialize = _.once(createApplication);
initialize();
initialize();
// Application is only created once.

http://underscorejs.org/#once


नहीं, यह बहुत बदसूरत है जब आप इसे तर्कों के साथ बुलाना शुरू करते हैं।
vsync

0
var init = function() {
    console.log("logges only once");
    init = false;
}; 

if(init) { init(); }

/* next time executing init() will cause error because now init is 
   -equal to false, thus typing init will return false; */

0

यदि आप रामदा का उपयोग कर रहे हैं, तो आप "एक बार" फ़ंक्शन का उपयोग कर सकते हैं ।

प्रलेखन से एक उद्धरण:

एक बार फ़ंक्शन (एक… → बी) → (एक… → बी) PARAMETERS v0.1.0 में जोड़ा गया

एक फंक्शन fn को स्वीकार करता है और एक फंक्शन देता है जो fn के इनवोकेशन को गार्ड करता है जैसे कि fn को कभी भी केवल एक बार कॉल किया जा सकता है, फिर चाहे कोई भी रिटर्न फंक्शन इनवॉच किया गया हो। परिकलित किए गए पहले मान को बाद के इनवोकेशन में लौटाया जाता है।

var addOneOnce = R.once(x => x + 1);
addOneOnce(10); //=> 11
addOneOnce(addOneOnce(50)); //=> 11

0

इसे यथासंभव सरल रखें

function sree(){
  console.log('hey');
  window.sree = _=>{};
}

आप परिणाम देख सकते हैं

स्क्रिप्ट परिणाम


यदि आप मॉड्यूल के अंदर हैं, तो thisइसके बजाय का उपयोग करेंwindow
श्रीकेथ के

0

JQuery विधि एक () का उपयोग करके केवल एक बार फ़ंक्शन को कॉल करने की अनुमति देता है :

let func = function() {
  console.log('Calling just once!');
}
  
let elem = $('#example');
  
elem.one('click', func);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
  <p>Function that can be called only once</p>
  <button id="example" >JQuery one()</button>
</div>

JQuery पद्धति का उपयोग करके कार्यान्वयन () :

let func = function(e) {
  console.log('Calling just once!');
  $(e.target).off(e.type, func)
}
  
let elem = $('#example');
  
elem.on('click', func);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
  <p>Function that can be called only once</p>
  <button id="example" >JQuery on()</button>
</div>

देशी जेएस का उपयोग कर कार्यान्वयन:

let func = function(e) {
  console.log('Calling just once!');
  e.target.removeEventListener(e.type, func);
}
  
let elem = document.getElementById('example');
  
elem.addEventListener('click', func);
<div>
  <p>Functions that can be called only once</p>
  <button id="example" >ECMAScript addEventListener</button>
</div>


-1
if (!window.doesThisOnce){
  function myFunction() {
    // do something
    window.doesThisOnce = true;
  };
};

वैश्विक स्कोप (उर्फ विंडो) को प्रदूषित करना एक बुरा अभ्यास है
vlio20 19

मैं आपसे सहमत हूं लेकिन किसी को इससे कुछ हो सकता है।
ATW

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

यह कहीं भी झूठ पर सेट नहीं है।
atw

-2

यह अनंत छोरों (jQuery का उपयोग करके) को रोकने के लिए उपयोगी है:

<script>
var doIt = true;
if(doIt){
  // do stuff
  $('body').html(String($('body').html()).replace("var doIt = true;", 
                                                  "var doIt = false;"));
} 
</script>

यदि आप नामस्थान प्रदूषण के बारे में चिंतित हैं, तो "doIt" के लिए एक लंबे, यादृच्छिक स्ट्रिंग को सब्सक्राइब करें।


-2

यह चिपचिपा निष्पादन को रोकने में मदद करता है

var done = false;

function doItOnce(func){
  if(!done){
    done = true;
    func()
  }
  setTimeout(function(){
    done = false;
  },1000)
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.