[英]Function in JavaScript that can be called only once
我需要創建一個只能執行一次的 function ,在第一次之后的每次都不會執行。 我從 C++ 和 Java 知道大約 static 變量可以完成這項工作,但我想知道是否有更優雅的方法來做到這一點?
如果“不會被執行”是指“多次調用時什么都不做”,則可以創建一個閉包:
var something = (function() {
var executed = false;
return function() {
if (!executed) {
executed = true;
// do something
}
};
})();
something(); // "do something" happens
something(); // nothing happens
回復@Vladloffe 的評論(現已刪除):使用全局變量,其他代碼可以重置“已執行”標志的值(無論您選擇什么名稱)。 使用閉包,其他代碼無法做到這一點,無論是無意的還是有意的。
正如這里的其他答案所指出的那樣,幾個庫(例如Underscore和Ramda )有一個小實用函數(通常命名為once()
[*] ),它接受一個函數作為參數並返回另一個函數,該函數只調用一次提供的函數,不管返回的函數被調用多少次。 返回的函數還緩存提供的函數首先返回的值,並在后續調用中返回該值。
但是,如果您不使用這樣的第三方庫,但仍然想要一個實用函數(而不是我上面提供的 nonce 解決方案),那么它很容易實現。 我見過的最好的版本是David Walsh 發布的:
function once(fn, context) {
var result;
return function() {
if (fn) {
result = fn.apply(context || this, arguments);
fn = null;
}
return result;
};
}
我傾向於改變fn = null;
到fn = context = null;
. 一旦fn
被調用,閉包就沒有理由保持對context
的引用。
用法:
function something() { /* do something */ }
var one_something = once(something);
one_something(); // "do something" happens
one_something(); // nothing happens
[*]但是請注意,其他庫,例如這個 Drupal 對 jQuery 的擴展,可能有一個名為once()
的函數,它做一些完全不同的事情。
將其替換為可重復使用的 NOOP (無操作)功能。
// this function does nothing
function noop() {};
function foo() {
foo = noop; // swap the functions
// do your thing
}
function bar() {
bar = noop; // swap the functions
// do your thing
}
function myFunc(){ myFunc = function(){}; // kill it as soon as it was called console.log('call once and never again!'); // your stuff here };
<button onClick=myFunc()>Call myFunc()</button>
var myFunc = function func(){ if( myFunc.fired ) return; myFunc.fired = true; console.log('called once and never again!'); // your stuff here }; // even if referenced & "renamed" ((refToMyfunc)=>{ setInterval(refToMyfunc, 1000); })(myFunc)
UnderscoreJs 有一個功能, underscorejs.org /#once
// Returns a function that will be executed at most one time, no matter how
// often you call it. Useful for lazy initialization.
_.once = function(func) {
var ran = false, memo;
return function() {
if (ran) return memo;
ran = true;
memo = func.apply(this, arguments);
func = null;
return memo;
};
};
談到靜態變量,這有點像閉包變體:
var once = function() {
if(once.done) return;
console.log('Doing this once!');
once.done = true;
};
once(); once();
然后,您可以根據需要重置功能:
once.done = false;
您可以簡單地使用“刪除自身”功能
function Once(){
console.log("run");
Once = undefined;
}
Once(); // run
Once(); // Uncaught TypeError: undefined is not a function
但是,如果您不想吞下錯誤,這可能不是最好的答案。
你也可以這樣做:
function Once(){
console.log("run");
Once = function(){};
}
Once(); // run
Once(); // nothing happens
我需要它像智能指針一樣工作,如果沒有 A 類型的元素可以執行,如果有一個或多個 A 元素,則無法執行函數。
function Conditional(){
if (!<no elements from type A>) return;
// do stuff
}
var quit = false;
function something() {
if(quit) {
return;
}
quit = true;
... other code....
}
嘗試這個
var fun = (function() {
var called = false;
return function() {
if (!called) {
console.log("I called");
called = true;
}
}
})()
來自一個名叫克羅克福德的家伙...... :)
function once(func) {
return function () {
var f = func;
func = null;
return f.apply(
this,
arguments
);
};
}
與setInterval
一起使用的可重用invalidate
函數:
var myFunc = function (){
if (invalidate(arguments)) return;
console.log('called once and never again!'); // your stuff here
};
const invalidate = function(a) {
var fired = a.callee.fired;
a.callee.fired = true;
return fired;
}
setInterval(myFunc, 1000);
在 JSBin 上試用: https ://jsbin.com/vicipar/edit?js,console
Bunyk的答案變化
簡單的裝飾器,在需要時易於編寫
function one(func) {
return function () {
func && func.apply(this, arguments);
func = null;
}
}
使用:
var initializer= one( _ =>{
console.log('initializing')
})
initializer() // 'initializing'
initializer() // nop
initializer() // nop
這是一個示例 JSFiddle - http://jsfiddle.net/6yL6t/
和代碼:
function hashCode(str) {
var hash = 0, i, chr, len;
if (str.length == 0) return hash;
for (i = 0, len = str.length; i < len; i++) {
chr = str.charCodeAt(i);
hash = ((hash << 5) - hash) + chr;
hash |= 0; // Convert to 32bit integer
}
return hash;
}
var onceHashes = {};
function once(func) {
var unique = hashCode(func.toString().match(/function[^{]+\{([\s\S]*)\}$/)[1]);
if (!onceHashes[unique]) {
onceHashes[unique] = true;
func();
}
}
你可以這樣做:
for (var i=0; i<10; i++) {
once(function() {
alert(i);
});
}
它只會運行一次:)
最初設定:
var once = function( once_fn ) {
var ret, is_called;
// return new function which is our control function
// to make sure once_fn is only called once:
return function(arg1, arg2, arg3) {
if ( is_called ) return ret;
is_called = true;
// return the result from once_fn and store to so we can return it multiply times:
// you might wanna look at Function.prototype.apply:
ret = once_fn(arg1, arg2, arg3);
return ret;
};
}
如果您使用 Node.js 或使用 browserify 編寫 JavaScript,請考慮使用“once”npm 模塊:
var once = require('once')
function load (file, cb) {
cb = once(cb)
loader.load('file')
loader.once('load', cb)
loader.once('error', cb)
}
如果您希望將來能夠重用該功能,那么根據上面 ed Hopp 的代碼,這可以很好地工作(我意識到最初的問題並沒有要求這個額外的功能!):
var something = (function() {
var executed = false;
return function(value) {
// if an argument is not present then
if(arguments.length == 0) {
if (!executed) {
executed = true;
//Do stuff here only once unless reset
console.log("Hello World!");
}
else return;
} else {
// otherwise allow the function to fire again
executed = value;
return;
}
}
})();
something();//Hello World!
something();
something();
console.log("Reset"); //Reset
something(false);
something();//Hello World!
something();
something();
輸出如下所示:
Hello World!
Reset
Hello World!
僅打開一次燈的簡單示例。
function turnOnLightOnce() {
let lightOn = false;
return function () {
if (!lightOn) {
console.log("Light is not on...Turning it on for first and last time");
lightOn = true;
}
};
}
const lightOn = turnOnLightOnce();
lightOn() // Light is not on...Turning it on for first and last time
lightOn()
lightOn()
lightOn()
lightOn()
https://codesandbox.io/s/javascript-forked-ojo0i?file=/index.js
這是由於 JavaScript 中的閉包造成的。
嘗試使用下划線“一次”功能:
var initialize = _.once(createApplication);
initialize();
initialize();
// Application is only created once.
var init = function() {
console.log("logges only once");
init = false;
};
if(init) { init(); }
/* next time executing init() will cause error because now init is
-equal to false, thus typing init will return false; */
if (!window.doesThisOnce){
function myFunction() {
// do something
window.doesThisOnce = true;
};
};
如果你使用 Ramda,你可以使用函數"once" 。
文檔中的引用:
一次函數 (a… → b) → (a… → b) 參數在 v0.1.0 中添加
接受一個函數 fn 並返回一個保護 fn 調用的函數,這樣 fn 只能被調用一次,無論返回的函數被調用多少次。 計算的第一個值在后續調用中返回。
var addOneOnce = R.once(x => x + 1);
addOneOnce(10); //=> 11
addOneOnce(addOneOnce(50)); //=> 11
盡可能簡單
function sree(){
console.log('hey');
window.sree = _=>{};
}
你可以看到結果
JQuery 只允許使用one()方法調用該函數一次:
let func = function() { console.log('Calling just once!'); } let elem = $('#example'); elem.one('click', func);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <div> <p>Function that can be called only once</p> <button id="example" >JQuery one()</button> </div>
使用 JQuery 方法on()實現:
let func = function(e) { console.log('Calling just once!'); $(e.target).off(e.type, func) } let elem = $('#example'); elem.on('click', func);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <div> <p>Function that can be called only once</p> <button id="example" >JQuery on()</button> </div>
使用原生 JS 實現:
let func = function(e) { console.log('Calling just once!'); e.target.removeEventListener(e.type, func); } let elem = document.getElementById('example'); elem.addEventListener('click', func);
<div> <p>Functions that can be called only once</p> <button id="example" >ECMAScript addEventListener</button> </div>
把我的帽子扔進戒指中以獲得樂趣,增加了記憶的優勢
const callOnce = (fn, i=0, memo) => () => i++ ? memo : (memo = fn());
// usage
const myExpensiveFunction = () => { return console.log('joe'),5; }
const memoed = callOnce(myExpensiveFunction);
memoed(); //logs "joe", returns 5
memoed(); // returns 5
memoed(); // returns 5
...
您可以使用 IIFE。 IIFE 表示立即調用函數表達式,結果是在創建時只調用一次函數。 您的代碼將如下所示:
(function () {
//The code you want to execute only one time etc...
console.log("Hello world");
})()
此外,通過這種方式,函數中的數據仍然是封裝的。
當然,您可以通過執行以下操作從函數返回值並將它們存儲到新變量中:
const/let value = (function () {
//The code you want to execute only one time etc...
const x = 10;
return x;
})()
function once (fn1) {
var ran = false
var memo = null
var fn = function(...args) {
if(ran) {return memo}
ran = true
memo = fn1.apply(null, args)
return memo
}
return fn
}
我正在使用帶有節點的打字稿,這是@I Hate Lazy 的回答啟發了我。 我剛剛將我的功能分配給了noop功能。
let printName = (name: string) => {
console.log(name)
printName = () => {}
}
printName('Sophia') // Sophia
printName('Nico') // Nothing Happens
如果 function 是事件監聽器的回調,那么addEventListner
方法中已經有一個內置選項,只執行一次callback
。
它可以接受3個參數
options
是一個 object ,它有一個名為once
的屬性
前任:
const button = document.getElementById('button'); const callbackFunc = () => { alert('run') } button.addEventListener('click', callbackFunc, { once: true })
<button id="button">Click Once</button>
function x()
{
let a=0;
return function check()
{
if(!a++)
{
console.log("This Function will execute Once.")
return;
}
console.log("You Can't Execute it For the Second Time.")
return;
}
}
z=x()
z() //Op - This Function will execute once
z() //OP - You can't Execute it for the second time.
我發現只使用一個只返回 true 一次的簡單 function 很有用,這樣您就可以將副作用保持得更高。
let once = () => !! (once = () => false);
once() // true
once() // false
像這樣使用:
if (once()) {
sideEffect()
}
這利用了這樣一個事實,即您可以強制賦值表達式返回true
,同時將相同的 function 更改為返回false
的 function。
如果你必須讓它執行 function,它可以使用三元來適應:
let once = (x) => !! (once = () => false) ? x() : false;
現在它接受一個 function 作為參數。 有趣的是,永遠不會達到第二個false
。
// This is how function in JavaScript can be called only once
let started = false;
if (!started) {
start() { // "do something" }
}
started = true;
}
這對於防止無限循環很有用(使用 jQuery):
<script>
var doIt = true;
if(doIt){
// do stuff
$('body').html(String($('body').html()).replace("var doIt = true;",
"var doIt = false;"));
}
</script>
如果您擔心命名空間污染,請用一個長的隨機字符串替換“doIt”。
它有助於防止粘性執行
var done = false;
function doItOnce(func){
if(!done){
done = true;
func()
}
setTimeout(function(){
done = false;
},1000)
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.