javascript中的函数只能被调用一次

时间:2012-10-03 17:18:10

标签: javascript function design-patterns

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

25 个答案:

答案 0 :(得分:187)

如果“不会被执行”你的意思是“在多次调用时不会做什么”,你可以创建一个闭包:

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解决方案),那么实现起来就很容易了。我见过的最好的版本是this one posted by 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的引用。

[*] 但请注意,其他库(例如this Drupal extension to jQuery)可能有一个名为once()的函数,它可以完成不同的操作。< /子>

答案 1 :(得分:58)

将其替换为可重复使用的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
}

答案 2 :(得分: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;
    };
  };

答案 3 :(得分:21)

一旦被调用,指向函数:

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

答案 4 :(得分:9)

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

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

once(); once(); 

如果您愿意,可以重置一个功能:

once.done = false;

答案 5 :(得分:4)

你可以简单地使用“自我删除”功能

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

答案 6 :(得分:4)

var quit = false;

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

答案 7 :(得分:2)

试试这个

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

答案 8 :(得分:2)

来自一些名叫Crockford的家伙... :)

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

答案 9 :(得分: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!

答案 10 :(得分:1)

可与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

answer from Bunyk

的变化

答案 11 :(得分:1)

如果您使用Node.js或使用browserify编写JavaScript,请考虑"once" npm module

var once = require('once')

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

答案 12 :(得分: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;
    };
}

答案 13 :(得分: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);
    });
}

它只会运行一次:)

答案 14 :(得分: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
...

答案 15 :(得分: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>

答案 16 :(得分:0)

保持尽可能简单

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

您可以看到结果

script result

答案 17 :(得分:0)

我带有简单的装饰器,可以轻松解决您的问题

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

使用:

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

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

答案 18 :(得分:0)

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

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

答案 19 :(得分: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

答案 20 :(得分: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; */

答案 21 :(得分:0)

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

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

http://underscorejs.org/#once

答案 22 :(得分:-1)

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

答案 23 :(得分:-2)

有助于防止粘性执行

var done = false;

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

答案 24 :(得分:-2)

这个用于防止无限循环(使用jQuery):

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

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