JavaScript 中只能调用一次的函数

IT技术 javascript function design-patterns
2021-01-21 03:10:32

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

6个回答

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

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

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

@Vaccano - 好的。现在我明白你在问什么了,我很高兴你找到了解决方案。本地存储解决方案是一种形式的全局变量(虽然乍一看可能不像)。这里的许多其他答案都使用类似的东西。我认为没有理由将全局变量方法与闭包结合起来(正如 Reflective 对您问题的回答所暗示的那样。)
2021-03-15 03:10:32
@EgyCode - 这在关于闭包MDN 文档中有很好的解释
2021-03-20 03:10:32
效果很好,能解释一下后面的登录吗,var是怎么执行的=false;作品
2021-03-30 03:10:32
抱歉,我的意思是逻辑,我从不理解布尔变量以及在这种情况下它是如何执行的
2021-04-03 03:10:32
@EgyCode - 在某些上下文中(例如在if语句测试表达式中),JavaScript 期望其中一个值trueorfalse并且程序流根据表达式计算时找到的值做出反应。条件运算符==总是评估为布尔值。变量也可以包含truefalse(有关更多信息,请参阅有关booleantruthyfalsey 的文档。)
2021-04-05 03:10:32

将其替换为可重复使用的 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
}
根据 asawyer 的回应,您只需要执行 _.once(foo) 或 _.once(bar),并且函数本身不需要知道只运行一次(不需要 noop 也不需要* = noop)。
2021-03-18 03:10:32
invalidatesetInterval一起使用的可重用功能。:jsbin.com/vicipar/1/edit?js,console
2021-03-23 03:10:32
@fableal:这怎么不优雅?同样,它非常干净,需要的代码更少,并且不需要为每个应该禁用的函数添加一个新变量。一个“空操作”的正是专为这种情况。
2021-03-24 03:10:32
并不是最好的解决方案。如果您将此函数作为回调传递,它仍然可以被多次调用。例如:setInterval(foo, 1000)- 这已经不再起作用了。您只是覆盖当前范围内的引用。
2021-04-02 03:10:32
@fableal:我只是看了 hakra 的回答。那么每次需要对新函数执行此操作时,都要创建一个新的闭包和变量?你对“优雅”有一个非常有趣的定义。
2021-04-09 03:10: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)

该解决方案更符合 Javascript 等高度动态语言的精神。为什么要设置信号量,一旦函数被使用就可以简单地清空它?
2021-03-12 03:10:32
这样做的问题是,如果在某处有另一个对该函数的引用(例如,它作为参数传递并隐藏在某个地方的另一个变量中——如在对 的调用中setInterval()),那么该引用将在调用时重复原始功能。
2021-03-27 03:10:32
@TedHopp -这是针对这些情况的特殊处理
2021-04-05 03:10:32
非常好的解决方案!该解决方案的性能也优于闭包方法。唯一的小“缺点”是如果名称更改,您需要保持函数名称同步。
2021-04-06 03:10:32
是的,这与Bunyk在此线程上的回答完全一样它也类似于闭包(如我的回答),但使用属性而不是闭包变量。这两种情况都与您在此答案中的方法大不相同。
2021-04-11 03:10:32

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;
    };
  };
@aschmied 你是对的 - 第一次调用的参数集的结果将被记忆并返回给所有其他调用,无论参数如何,因为底层函数永远不会再次调用。在这种情况下,我不建议使用该_.once方法。参见jsfiddle.net/631tgc5f/1
2021-03-22 03:10:32
once接受论点对我来说似乎很有趣您可以执行squareo = _.once(square); console.log(squareo(1)); console.log(squareo(2));和获取1对 的两个调用squareo我理解正确吗?
2021-03-25 03:10:32
@aschmied 或者我想对每个参数集单独调用一次。我不认为这真的是为了那种用途。
2021-03-25 03:10:32
如果您已经在使用_则很方便我不建议依赖整个库来编写这么小的代码。
2021-03-28 03:10:32

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

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

once(); once(); 

如果你愿意,你可以重置一个函数:

once.done = false;