簡體   English   中英

Function in JavaScript 只能調用一次

[英]Function in JavaScript that can be called only once

我需要創建一個只能執行一次的 function ,在第一次之后的每次都不會執行。 我從 C++ 和 Java 知道大約 static 變量可以完成這項工作,但我想知道是否有更優雅的方法來做到這一點?

如果“不會被執行”是指“多次調用時什么都不做”,則可以創建一個閉包:

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

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

回復@Vladloffe 的評論(現已刪除):使用全局變量,其他代碼可以重置“已執行”標志的值(無論您選擇什么名稱)。 使用閉包,其他代碼無法做到這一點,無論是無意的還是有意的。

正如這里的其他答案所指出的那樣,幾個庫(例如UnderscoreRamda )有一個小實用函數(通常命名為once() [*] ),它接受一個函數作為參數並返回另一個函數,該函數只調用一次提供的函數,不管返回的函數被調用多少次。 返回的函數還緩存提供的函數首先返回的值,並在后續調用中返回該值。

但是,如果您不使用這樣的第三方庫,但仍然想要一個實用函數(而不是我上面提供的 nonce 解決方案),那么它很容易實現。 我見過的最好的版本是David Walsh 發布的

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; . 一旦fn被調用,閉包就沒有理由保持對context的引用。

用法:

function something() { /* do something */ }
var one_something = once(something);

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

[*]但是請注意,其他庫,例如這個 Drupal 對 jQuery 的擴展,可能有一個名為once()的函數,它做一些完全不同的事情。

將其替換為可重復使用的 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
}

調用后指向一個函數:

 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)

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;
    };
  };

談到靜態變量,這有點像閉包變體:

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

once(); once(); 

然后,您可以根據需要重置功能:

once.done = false;

您可以簡單地使用“刪除自身”功能

​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

我需要它像智能指針一樣工作,如果沒有 A 類型的元素可以執行,如果有一個或多個 A 元素,則無法執行函數。

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

    // do stuff
}
var quit = false;

function something() {
    if(quit) {
       return;
    } 
    quit = true;
    ... other code....
}

嘗試這個

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

來自一個名叫克羅克福德的家伙...... :)

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

setInterval一起使用的可重用invalidate函數:

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的答案變化

簡單的裝飾器,在需要時易於編寫

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

使用:

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

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

這是一個示例 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);
    });
}

它只會運行一次:)

最初設定:

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;
    };
}

如果您使用 Node.js 或使用 browserify 編寫 JavaScript,請考慮使用“once”npm 模塊

var once = require('once')

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

如果您希望將來能夠重用該功能,那么根據上面 ed Hopp 的代碼,這可以很好地工作(我意識到最初的問題並沒有要求這個額外的功能!):

   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!

僅打開一次燈的簡單示例。

function turnOnLightOnce() {
  let lightOn = false;

  return function () {
    if (!lightOn) {
      console.log("Light is not on...Turning it on for first and last time");
      lightOn = true;
    }

  };
}

const lightOn = turnOnLightOnce();
lightOn()  // Light is not on...Turning it on for first and last time
lightOn()
lightOn()
lightOn()
lightOn()

https://codesandbox.io/s/javascript-forked-ojo0i?file=/index.js

這是由於 JavaScript 中的閉包造成的。

嘗試使用下划線“一次”功能:

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

http://underscorejs.org/#once

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; */
if (!window.doesThisOnce){
  function myFunction() {
    // do something
    window.doesThisOnce = true;
  };
};

如果你使用 Ramda,你可以使用函數"once"

文檔中的引用:

一次函數 (a… → b) → (a… → b) 參數在 v0.1.0 中添加

接受一個函數 fn 並返回一個保護 fn 調用的函數,這樣 fn 只能被調用一次,無論返回的函數被調用多少次。 計算的第一個值在后續調用中返回。

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

盡可能簡單

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

你可以看到結果

腳本結果

JQuery 只允許使用one()方法調用該函數一次:

 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 方法on()實現:

 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>

使用原生 JS 實現:

 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>

把我的帽子扔進戒指中以獲得樂趣,增加了記憶的優勢

const callOnce = (fn, i=0, memo) => () => i++ ? memo : (memo = fn());
// usage
const myExpensiveFunction = () => { return console.log('joe'),5; }
const memoed = callOnce(myExpensiveFunction);
memoed(); //logs "joe", returns 5
memoed(); // returns 5
memoed(); // returns 5
...

您可以使用 IIFE。 IIFE 表示立即調用函數表達式,結果是在創建時只調用一次函數。 您的代碼將如下所示:

(function () {
  //The code you want to execute only one time etc...
  console.log("Hello world");
})()

此外,通過這種方式,函數中的數據仍然是封裝的。
當然,您可以通過執行以下操作從函數返回值並將它們存儲到新變量中:

const/let value = (function () {
  //The code you want to execute only one time etc...
  const x = 10;
  return x;
})()
function once (fn1) {
  var ran = false
  var memo = null
  var fn = function(...args) {
    if(ran) {return memo}
    ran = true
    memo = fn1.apply(null, args)
    return memo
  }
  return fn
}

我正在使用帶有節點的打字稿,這是@I Hate Lazy 的回答啟發了我。 我剛剛將我的功能分配給了noop功能。

let printName = (name: string) => {
    console.log(name)
    printName = () => {}
}

printName('Sophia') // Sophia
printName('Nico')   // Nothing Happens

https://jsbin.com/yuzicek/edit?js,console

特殊情況 - 如果 FUNCTION 是回調事件處理程序

如果 function 是事件監聽器的回調,那么addEventListner方法中已經有一個內置選項,只執行一次callback

它可以接受3個參數

  • 類型
  • 打回來
  • 選項

options是一個 object ,它有一個名為once的屬性

前任:

 const button = document.getElementById('button'); const callbackFunc = () => { alert('run') } button.addEventListener('click', callbackFunc, { once: true })
 <button id="button">Click Once</button>

function x()
{
  let a=0;
  return function check()
   {
    if(!a++) 
     {
       console.log("This Function will execute Once.")
       return;
      }
     console.log("You Can't Execute it For the Second Time.")
     return;
    }
  }

 z=x()
 z() //Op - This Function will execute once
 z() //OP - You can't Execute it for the second time.

我發現只使用一個只返回 true 一次的簡單 function 很有用,這樣您就可以將副作用保持得更高。

let once = () => !! (once = () => false);

once() // true
once() // false

像這樣使用:

if (once()) {
  sideEffect()
}

這利用了這樣一個事實,即您可以強制賦值表達式返回true ,同時將相同的 function 更改為返回false的 function。

如果你必須讓它執行 function,它可以使用三元來適應:

let once = (x) => !! (once = () => false) ? x() : false;

現在它接受一個 function 作為參數。 有趣的是,永遠不會達到第二個false

// This is how function in JavaScript can be called only once    

let started = false;     
      if (!started) {
                    start() { // "do something" }
                }
                started = true;
            }

這對於防止無限循環很有用(使用 jQuery):

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

如果您擔心命名空間污染,請用一個長的隨機字符串替換“doIt”。

它有助於防止粘性執行

var done = false;

function doItOnce(func){
  if(!done){
    done = true;
    func()
  }
  setTimeout(function(){
    done = false;
  },1000)
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM