【问题标题】:JavaScript - run once without booleansJavaScript - 运行一次,不带布尔值
【发布时间】:2011-04-10 08:50:11
【问题描述】:

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

具体不是类似:

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

  /* do stuff here */

}

我将拥有很多这类函数,而保留所有布尔值会很麻烦...

【问题讨论】:

  • 你需要再澄清一下这个问题。
  • 但是如果不止一次,人们会打电话,我希望计数器只激活一次。
  • 也许你应该编辑你的问题并澄清这是关于什么的。你有函数作为事件处理程序,你只想调用一次?
  • 一旦你理解了,这不是一个坏问题。
  • 丹这是作业吗?说明你想在哪里应用它,否则答案将永远无法满足你的需求。

标签: optimization javascript


【解决方案1】:

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

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();

“有用”示例:

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!" : ":)");

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

【讨论】:

  • 确实是+1!赋予“此功能首次使用后自毁”新含义。
  • 非常整洁,虽然我看不到它的价值。虽然这不是你的错 +1
  • 我为 mcgrailm 添加了一个示例 :)
  • 我已经回答了您的问题“在不使用布尔值的情况下运行一次代码”。如果这不能回答您的问题,请提供包含您当前情况、问题和所需情况的代码示例。
  • 我会强烈反对使用这种模式,因为第一个函数的所有变量、参数和内部 FunctionDeclarations 将是 never 被垃圾回收,这可能并不明显,但确实如此,空函数是在第一个函数中创建的,这会创建一个闭包,所有标识符将保持 live,创建内存泄漏。我建议要么在外面声明空函数,要么使用 Function 构造函数,例如useThisFunctionOnce = Function("");。试试this test 看看所有变量如何有效地保持在范围内。
【解决方案2】:

我喜欢 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();

【讨论】:

  • (注:我还是更喜欢 Lekensteyn 的实现)
  • 解析函数名的三行代码有点重。我推荐一个简单的正则表达式解决方案:functionName = func.toString().match(/\w+(?=\()/)[0]
  • @Mike Robinson - 为什么runAgain() 有两个函数定义?
  • @bennedich - 我喜欢正则表达式的想法,但你提供的那个不起作用,当我修复它时返回“函数”
  • @Mike 这很公平。我实际上不确定这是否是诡计的一部分——不是想成为一个聪明的驴子;-)
【解决方案3】:

其中很多方法的一个问题是它们依赖于函数名称来工作:如果您使用“x = function() ...”创建一个函数,Mike 的方法将失败,如果您设置 Lekensteyn 的方法将失败x = useThisFunctionOnce 在调用 useThisFunctionOnce 之前。

如果你想立即运行,我建议使用 Russ 的关闭方法,如果你想延迟执行,我建议使用 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);

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

【讨论】:

  • 我一直在努力理解Underscoreonce,但我很困惑。在第二次调用中,ran 不是重新声明并初始化为false,从而第二次调用func.apply()
  • @Seabiscuit 一次返回内部函数,run 在外部函数中定义。结果是一个闭包,其中 run 和 memo 由对函数的所有调用共享。
  • 我想这就是它的工作原理。我仍然不明白的是,当调用 once(func) 外部函数中的变量赋值时(即 ran = false 不影响return 函数中ran 的值。由于每次调用once(func) 都会调用外部和内部函数,因此每次调用once(func) 时都会分配ran = false
  • @Seabiscuit 闭包在任何时候调用函数并且其范围内的函数超过它时都会创建。如果我调用once 两次,我会得到两个闭包,每个闭包都有自己的ran 值。如果我调用返回的函数,它们会在自己的闭包范围内设置run = true,并且不会相互干扰。
【解决方案4】:

立即调用的匿名函数呢?

(function () {

    // code in here to run once

})();

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

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

【讨论】:

    【解决方案5】:

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

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

    这与所有其他答案非常接近,但它有点自包含代码,您可以独立使用它,这很好。我仍在尝试掌握所有替换机制,但实际上它工作得很好。

    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");
    

    对于那些对此感到好奇的人,jsbin 转换

    【讨论】:

      【解决方案6】:
      (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();
      
      })();
      

      【讨论】:

        【解决方案7】:

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

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

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

        【讨论】:

          【解决方案8】:

          此外,“/* do stuff here */”中发生的事情的性质可能会留下一些东西,如果存在的话,一定意味着函数已经运行,例如

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

          【讨论】:

            【解决方案9】:

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

            【讨论】:

            • 很简单,考虑到这个问题。
            猜你喜欢
            • 1970-01-01
            • 2021-11-24
            • 1970-01-01
            • 1970-01-01
            • 2010-11-20
            • 2011-11-04
            • 1970-01-01
            • 2020-07-03
            • 2018-11-03
            相关资源
            最近更新 更多