[英]JavaScript - run once without booleans
有沒有辦法只運行一段JavaScript代碼ONCE ,而不使用布爾標志變量來記住它是否已經運行過?
特別是不是這樣的:
var alreadyRan = false;
function runOnce() {
if (alreadyRan) {
return;
}
alreadyRan = true;
/* do stuff here */
}
我將有很多這些類型的功能,並保持所有布爾將是凌亂的......
在執行時覆蓋函數的另一種方法,它只執行一次。
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("")
。 這將在全局范圍內創建一個空函數,從而避免閉包。
我喜歡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();
很多這些方法的問題在於它們依賴於函數名稱來工作:如果使用“x = function()...”創建函數,Mike的方法將失敗,如果設置x = useThisFunctionOnce,則Lekensteyn的方法將失敗在使用之前調用ThisFunctionOnce。
如果你想運行它馬上或所采取的方法,我會建議使用拉斯的關閉方法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);
在第一次執行時,執行內部函數並返回結果。 在后續運行中,將返回原始結果對象。
如何立即調用匿名函數?
(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();
道格拉斯·克羅克福德 ( Douglas Crockford)的優雅解決方案花了一些時間來理解它是如何工作的,並偶然發現了這個線
所以包裝器一旦返回函數就會調用你傳遞的參數函數。 並且利用閉包這個構造在第一次調用之后將傳遞的函數替換為空函數,或者在原始源中將null替換為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變換
我剛遇到這個問題,最后做了類似下面的事情:
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屬性的事實。
(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();
})();
另外,“/ * do stuff here / /”中發生的事情的性質可能會留下一些東西,當存在時,必須意味着函數已經運行,例如
var counter = null;
function initCounter() {
if (counter === null) {
counter = 0;
}
}
如果沒有綁定到事件,代碼通常運行一次
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.