135

我需要创建一个只能执行一次的函数,在第一次之后的每次都不会执行。我从 C++ 和 Java 知道可以完成这项工作的静态变量,但我想知道是否有更优雅的方法来做到这一点?

4

25 回答 25

269

如果“不会被执行”是指“多次调用时什么都不做”,则可以创建一个闭包:

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;. 没有理由让闭包保持对context一次fn被调用的引用。

用法:

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

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

[*] 但是请注意,其他库,例如这个 Drupal 对 jQuery 的扩展,可能有一个名为的函数once(),它的作用完全不同。

于 2012-10-03T17:20:49.053 回答
69

将其替换为可重复使用的 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
}
于 2012-10-03T17:20:45.303 回答
40

调用后指向一个函数:

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)

于 2014-05-15T17:44:53.223 回答
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;
    };
  };
于 2012-10-03T17:20:18.273 回答
12

谈到静态变量,这有点像闭包变体:

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

once(); once(); 

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

once.done = false;
于 2016-03-14T10:12:06.200 回答
5

您可以简单地使用“删除自身”功能

​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
}
于 2012-10-03T17:26:38.750 回答
4
var quit = false;

function something() {
    if(quit) {
       return;
    } 
    quit = true;
    ... other code....
}
于 2012-10-03T17:19:27.943 回答
2

From some dude named Crockford... :)

function once(func) {
    return function () {
        var f = func;
        func = null;
        return f.apply(
            this,
            arguments
        );
    };
}
于 2014-04-16T10:26:48.950 回答
2

尝试这个

var fun = (function() {
  var called = false;
  return function() {
    if (!called) {
      console.log("I  called");
      called = true;
    }
  }
})()
于 2012-10-03T17:22:58.550 回答
2

Reusable invalidate function which works with 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);

Try it on JSBin: https://jsbin.com/vicipar/edit?js,console

Variation of answer from Bunyk

于 2017-04-27T23:50:45.253 回答
2

简单的装饰器,在需要时易于编写

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

使用:

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

initializer() // 'initializing'
initializer() // nop
initializer() // nop
于 2018-07-14T08:12:49.857 回答
1

如果您使用 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)
}
于 2014-12-09T12:02:17.703 回答
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;
    };
}
于 2014-10-28T08:55:47.597 回答
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);
    });
}

它只会运行一次:)

于 2014-07-02T14:54:47.017 回答
1

如果您希望将来能够重用该功能,那么根据上面 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!
于 2018-03-21T01:31:10.637 回答
1

仅打开一次灯的简单示例。

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 中的闭包造成的。

于 2021-03-04T04:53:31.500 回答
0

尝试使用下划线“一次”功能:

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

http://underscorejs.org/#once

于 2014-04-17T11:15:34.107 回答
0

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>

于 2020-04-07T19:47:25.537 回答
0

尽可能简单

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

你可以看到结果

脚本结果

于 2019-08-20T09:52:53.613 回答
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; */
于 2016-01-13T17:58:03.980 回答
0

如果你使用 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
于 2017-05-25T16:01:32.510 回答
0
if (!window.doesThisOnce){
  function myFunction() {
    // do something
    window.doesThisOnce = true;
  };
};
于 2016-02-12T14:32:57.077 回答
0

把我的帽子扔进戒指中以获得乐趣,增加了记忆的优势

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
...
于 2020-08-23T00:42:07.333 回答
-2

这对于防止无限循环很有用(使用 jQuery):

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

如果您担心命名空间污染,请用一个长的随机字符串替换“doIt”。

于 2016-04-01T23:51:56.620 回答
-2

它有助于防止粘性执行

var done = false;

function doItOnce(func){
  if(!done){
    done = true;
    func()
  }
  setTimeout(function(){
    done = false;
  },1000)
}
于 2016-05-11T08:45:37.697 回答