JavaScript - запускается один раз без булевых
Есть ли способ запустить кусок кода JavaScript только ONCE, не используя логические переменные флагов, чтобы помнить, был ли он уже запущен или нет?
В частности не что-то вроде:
var alreadyRan = false;
function runOnce() {
if (alreadyRan) {
return;
}
alreadyRan = true;
/* do stuff here */
}
У меня будет много этих типов функций, и все булевы будут беспорядочными...
Ответы
Ответ 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
Если это не связано с событием, код обычно запускается один раз