Подтвердить что ты не робот

JavaScript - запускается один раз без булевых

Есть ли способ запустить кусок кода JavaScript только ONCE, не используя логические переменные флагов, чтобы помнить, был ли он уже запущен или нет?

В частности не что-то вроде:

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

  /* do stuff here */

}

У меня будет много этих типов функций, и все булевы будут беспорядочными...

4b9b3361

Ответ 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 wrong wth you?!");
}
alert(readPreference('is_stupid') ? "Stupid!" : ":)");

Изменить: как указывала CMS, просто переписать старую функцию с помощью function(){} создаст закрытие, в котором все еще существуют старые переменные. Чтобы обойти эту проблему, function(){} заменяется на Function(""). Это создаст пустую функцию в глобальной области видимости, избегая закрытия.

Ответ 2

Мне нравится реализация Lekensteyn, но вы можете просто иметь одну переменную для хранения функций, которые выполнялись. Код ниже должен запускать "runOnce" и "runAgain" как один раз. Он по-прежнему имеет значение boolean, но похоже, что вы просто не хотите много переменных.

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

Ответ 3

Проблема с довольно многими из этих подходов заключается в том, что они зависят от имен функций для работы: подход Майка завершится неудачно, если вы создадите функцию с помощью функции "x = function()...", и подход Lekensteyn завершится неудачно, если вы установите x = useThisFunctionOnce перед использованиемThisFunctionOnce вызывается.

Я бы порекомендовал использовать подход 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);

При первом выполнении выполняется внутренняя функция и возвращаются результаты. При последующих запусках возвращается исходный объект результата.

Ответ 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

Элегантное решение от Дуглас Крокфорд потратило некоторое время, чтобы понять, как оно работает и наткнулось на эту тему.

Итак, когда-то функция-оболочка возвращает функцию, которая просто вызывает функцию параметра, которую вы передали. И, воспользовавшись закрытием, эта конструкция заменила переданную функцию на пустую функцию или нуль в исходном источнике после первого вызова, поэтому все последующие вызовы будут бесполезны.

Это нечто очень близкое ко всем другим ответам, но это своего рода код, содержащий код, и вы можете использовать его независимо, что хорошо. Я все еще пытаюсь понять весь механизм замены, но практически он просто отлично работает.

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 undefined.

Ответ 8

Кроме того, природа того, что происходит в "/* do stuff here */", может оставить что-то вокруг этого, когда оно присутствует, должно означать, что функция выполнялась, например.

var counter = null;

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

Ответ 9

Если это не связано с событием, код обычно запускается один раз