JavaScript - 在没有布尔值的情况下运行一次

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3678628/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-25 01:55:01  来源:igfitidea点击:

JavaScript - run once without booleans

optimizationjavascript

提问by test

Is there a way to run a piece of JavaScript code only ONCE, without using boolean flag variables to remember whether it has already been ran or not?

有没有办法只运行一段 JavaScript 代码ONCE,而不使用布尔标志变量来记住它是否已经运行过?

Specifically notsomething like:

具体不是这样的:

var alreadyRan = false;
function runOnce() {
  if (alreadyRan) {
    return;
  }
  alreadyRan = true;

  /* do stuff here */

}

I'm going to have a lot of these types of functions and keeping all booleans would be messy...

我将有很多这些类型的函数,并且保留所有布尔值会很混乱......

回答by Lekensteyn

An alternative way that overwrites a function when executed so it will be executed only once.

在执行时覆盖函数的另一种方法,因此它只会执行一次。

function useThisFunctionOnce(){
   // overwrite this function, so it will be executed only once
   useThisFunctionOnce = Function("");
   // real code below
   alert("Hi!");
}

// displays "Hi!"
useThisFunctionOnce();
// does nothing
useThisFunctionOnce();

'Useful' example:

“有用”的例子:

var preferences = {};
function read_preferences(){
   // read preferences once
   read_preferences = Function("");
   // load preferences from storage and save it in 'preferences'
}
function readPreference(pref_name){
    read_prefences();
    return preferences.hasOwnProperty(pref_name) ? preferences[pref_name] : '';
}
if(readPreference('like_javascript') != 'yes'){
   alert("What's wrong wth you?!");
}
alert(readPreference('is_stupid') ? "Stupid!" : ":)");


Edit: as CMS pointed out, just overwriting the old function with function(){}will create a closure in which old variables still exist. To work around that problem, function(){}is replaced by Function(""). This will create an empty function in the global scope, avoiding a closure.

编辑:正如 CMS 所指出的,只需用 覆盖旧函数function(){}就会创建一个旧变量仍然存在的闭包。为了解决这个问题,function(){}被替换为Function(""). 这将在全局范围内创建一个空函数,避免关闭。

回答by Mike Robinson

I like Lekensteyn's implementation, but you could also just have one variable to store what functions have run. The code below should run "runOnce", and "runAgain" both one time. It's still booleans, but it sounds like you just don't want lots of variables.

我喜欢 Lekensteyn 的实现,但您也可以只使用一个变量来存储已运行的函数。下面的代码应该运行一次“runOnce”和“runAgain”。它仍然是布尔值,但听起来你只是不想要很多变量。

var runFunctions = {};

function runOnce() {
  if(!hasRun(arguments.callee)) {
   /* do stuff here */
   console.log("once");
  }
}

function runAgain() {
  if(!hasRun(arguments.callee)) {
   /* do stuff here */
   console.log("again");
  }
}


function hasRun(functionName) {
 functionName = functionName.toString();
 functionName = functionName.substr('function '.length);
 functionName = functionName.substr(0, functionName.indexOf('('));

 if(runFunctions[functionName]) {
   return true;
 } else {
   runFunctions[functionName] = true;
   return false;
 }
}

runOnce();
runAgain();
runAgain();

回答by Brian Nickel

A problem with quite a few of these approaches is that they depend on function names to work: Mike's approach will fail if you create a function with "x = function() ..." and Lekensteyn's approach will fail if you set x = useThisFunctionOnce before useThisFunctionOnce is called.

很多这些方法的一个问题是它们依赖于函数名来工作:如果你创建一个带有“x = function() ...”的函数,Mike 的方法就会失败,如果你设置 x = useThisFunctionOnce,Lekensteyn 的方法就会失败在调用 useThisFunctionOnce 之前。

I would recommend using Russ's closure approach if you want it run right away or the approach taken by Underscore.jsif you want to delay execution:

如果你想运行它马上或所采取的方法,我会建议使用拉斯的关闭方法Underscore.js如果要延迟执行:

function once(func) {
    var ran = false, memo;
    return function() {
        if (ran) return memo;
        ran = true;
        return memo = func.apply(this, arguments);
    };
}

var myFunction = once(function() {
    return new Date().toString();
});

setInterval(function() {console.log(myFunction());}, 1000);

On the first execution, the inner function is executed and the results are returned. On subsequent runs, the original result object is returned.

第一次执行时,会执行内部函数并返回结果。在后续运行中,将返回原始结果对象。

回答by Russ Cam

What about an immediately invoked anonymous function?

立即调用的匿名函数怎么样?

(function () {

    // code in here to run once

})();

the code will execute immediately and leave no trace in the global namespace.

代码将立即执行并且不会在全局命名空间中留下任何痕迹。

If this code is going to need to be called from elsewhere, then a closure can be used to ensure that the contents of a function are run only once. Personally, I prefer this to a function that rewrites itself as I feel doing so can cause confusion, but to each their own :) This particular implementation takes advantage of the fact that 0 is a falsy value.

如果需要从其他地方调用此代码,则可以使用闭包来确保函数的内容只运行一次。就我个人而言,我更喜欢这个而不是一个重写自己的函数,因为我觉得这样做会引起混乱,但对于每个人来说:) 这个特定的实现利用了 0 是一个假值的事实。

var once = (function() {
  var hasRun = 0;  
  return function () {
    if (!hasRun) {
      hasRun++;   

      // body to run only once

      // log to the console for a test       
      console.log("only ran once");
    }              
  }
})();

// test that the body of the function executes only once
for (var i = 0; i < 5; i++) 
  once();

回答by dmi3y

Elegant solution from Douglas Crockford, spent some time to understand how it works and stumbled upon this thread.

来自Douglas Crockford 的优雅解决方案,花了一些时间来了解它是如何工作的,并偶然发现了这个线程。

So the wrapper once return function which is just invokes parameter's function you passed. And taking advantage of closures this construction replaced passed function to empty function, or null in original source, after the first call, so all the next calls will be useless.

所以包装器一次返回函数,它只是调用你传递的参数函数。并利用闭包,这种构造在第一次调用后将传递的函数替换为空函数,或原始源中的 null,因此所有接下来的调用都将无用。

This is something very close to all other answers, but it is kinda self containing code and you could use it independently, which is good. I am still trying to grasp all the entire mechanism of replacement, but practically it just works perfectly.

这与所有其他答案非常接近,但它有点自包含代码,您可以独立使用它,这很好。我仍然试图掌握所有替换的整个机制,但实际上它只是完美地工作。

function once (func) {

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

}

function hi(name) {
  console.log("Hi %s", name);
}

sayonce = once(hi);
sayonce("Vasya");
sayonce("Petya");

for those who are curious here is jsbintransformations

对于那些好奇的人来说,jsbin转换

回答by Kevin

I just ran into this problem, and ended up doing something like the following:

我刚刚遇到了这个问题,最后做了如下事情:

function runOnce () {
    if (!this.alreadyRan) {
        // put all your functionality here
        console.log('running my function!');

        // set a property on the function itself to prevent it being run again
        this.alreadyRan = true;
    }
}

This takes advantage of the fact that Javascript properties are undefined by default.

这利用了默认情况下未定义 Javascript 属性的事实。

回答by tere?ko

(function (){

  var run = (function (){

    var func, blank = function () {};

    func = function () {
      func = blank;

      // following code executes only once 
      console.log('run once !');
    };

    return function(){
      func.call();
    };
  })();

  run();
  run();
  run();
  run();

})();

回答by Christopher Hunt

In addition, the nature of what happens in the "/* do stuff here */" may leave something around that, when present, must mean that the function has run e.g.

此外,在“/* do stuff here */”中发生的事情的本质可能会留下一些东西,当存在时,必须意味着该函数已经运行,例如

var counter = null;

function initCounter() {
  if (counter === null) {
    counter = 0;
  }
}

回答by Z. Zlatev

If not bound to an event, code is usually ran once

如果没有绑定到事件,代码通常会运行一次