Devo creare una funzione che possa essere eseguita una sola volta, ogni volta dopo la prima non verrà eseguita. Conosco da C ++ e Java le variabili statiche che possono fare il lavoro, ma vorrei sapere se esiste un modo più elegante per farlo?
Devo creare una funzione che possa essere eseguita una sola volta, ogni volta dopo la prima non verrà eseguita. Conosco da C ++ e Java le variabili statiche che possono fare il lavoro, ma vorrei sapere se esiste un modo più elegante per farlo?
Risposte:
Se con "non verrà eseguito" intendi "non farà nulla se chiamato più di una volta", puoi creare una chiusura:
var something = (function() {
var executed = false;
return function() {
if (!executed) {
executed = true;
// do something
}
};
})();
something(); // "do something" happens
something(); // nothing happens
In risposta a un commento di @Vladloffe (ora eliminato): con una variabile globale, un altro codice potrebbe reimpostare il valore del flag "eseguito" (qualunque sia il nome scelto). Con una chiusura, un altro codice non ha modo di farlo, accidentalmente o deliberatamente.
Come altre risposte qui sottolineano, diverse librerie (come Underscore e Ramda ) hanno una piccola funzione di utilità (tipicamente chiamata once()
[*] ) che accetta una funzione come argomento e restituisce un'altra funzione che chiama la funzione fornita esattamente una volta, indipendentemente da come molte volte viene chiamata la funzione restituita. La funzione restituita memorizza anche nella cache il valore restituito per primo dalla funzione fornita e lo restituisce nelle chiamate successive.
Tuttavia, se non stai utilizzando una libreria di terze parti, ma desideri comunque una tale funzione di utilità (piuttosto che la soluzione nonce che ho offerto sopra), è abbastanza facile da implementare. La versione più bella che ho visto è questa pubblicata da David Walsh :
function once(fn, context) {
var result;
return function() {
if (fn) {
result = fn.apply(context || this, arguments);
fn = null;
}
return result;
};
}
Sarei propenso a cambiare fn = null;
in fn = context = null;
. Non c'è motivo per la chiusura di mantenere un riferimento a context
una volta che fn
è stato chiamato.
[*] Tieni presente, tuttavia, che altre librerie, come questa estensione Drupal a jQuery , potrebbero avere una funzione denominata once()
che fa qualcosa di completamente diverso.
if
un'espressione di test di istruzione), JavaScript si aspetta uno dei valori true
o false
e il flusso del programma reagisce in base al valore trovato quando l'espressione viene valutata. Gli operatori condizionali come ==
restituiscono sempre un valore booleano. Una variabile può contenere anche true
o false
. (Per ulteriori informazioni, vedere la documentazione su booleano , veritiero e falso .)
Sostituirlo con una funzione NOOP (nessuna operazione) riutilizzabile .
// 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
}
setInterval(foo, 1000)
- e già questo non funziona più. Stai solo sovrascrivendo il riferimento nell'ambito corrente.
invalidate
Funzione riutilizzabile che funziona con setInterval
ecc.: Jsbin.com/vicipar/1/edit?js,console
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)
setInterval()
), il riferimento ripeterà la funzionalità originale quando viene chiamato.
UnderscoreJs ha una funzione che lo fa, 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;
};
};
once
accettare argomenti. Potresti fare squareo = _.once(square); console.log(squareo(1)); console.log(squareo(2));
e ottenere 1
entrambe le chiamate a squareo
. Lo capisco bene?
_.once
metodo. Vedi jsfiddle.net/631tgc5f/1
_
; Non consiglierei di dipendere dall'intera libreria per un po 'di codice così piccolo.
var quit = false;
function something() {
if(quit) {
return;
}
quit = true;
... other code....
}
Potresti semplicemente avere la funzione "rimuovi se stesso"
function Once(){
console.log("run");
Once = undefined;
}
Once(); // run
Once(); // Uncaught TypeError: undefined is not a function
Ma questa potrebbe non essere la risposta migliore se non vuoi inghiottire errori.
Potresti anche fare questo:
function Once(){
console.log("run");
Once = function(){};
}
Once(); // run
Once(); // nothing happens
Ho bisogno che funzioni come un puntatore intelligente, se non ci sono elementi del tipo A può essere eseguito, se ci sono uno o più elementi A la funzione non può essere eseguita.
function Conditional(){
if (!<no elements from type A>) return;
// do stuff
}
Once
viene passato come call-back (ad esempio setInterval(Once, 100)
). La funzione originale continuerà a essere chiamata.
prova questo
var fun = (function() {
var called = false;
return function() {
if (!called) {
console.log("I called");
called = true;
}
}
})()
Da un tizio di nome Crockford ... :)
function once(func) {
return function () {
var f = func;
func = null;
return f.apply(
this,
arguments
);
};
}
TypeError: Cannot read property 'apply' of null
sia fantastico. Questo è ciò che ottieni la seconda volta che invochi la funzione restituita.
invalidate
Funzione riutilizzabile che funziona con setInterval
:
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);
Provalo su JSBin: https://jsbin.com/vicipar/edit?js,console
Variazione della risposta di Bunyk
Ecco un esempio di JSFiddle: http://jsfiddle.net/6yL6t/
E il codice:
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();
}
}
Potresti fare:
for (var i=0; i<10; i++) {
once(function() {
alert(i);
});
}
E verrà eseguito solo una volta :)
Configurazione iniziale:
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;
};
}
Se utilizzi Node.js o scrivi JavaScript con browserify, considera il modulo npm "una volta" :
var once = require('once')
function load (file, cb) {
cb = once(cb)
loader.load('file')
loader.once('load', cb)
loader.once('error', cb)
}
Se vuoi essere in grado di riutilizzare la funzione in futuro, questo funziona bene sulla base del codice di ed Hopp sopra (mi rendo conto che la domanda originale non richiedeva questa funzione extra!):
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();
L'output è simile a:
Hello World!
Reset
Hello World!
decoratore semplice che facile da scrivere quando serve
function one(func) {
return function () {
func && func.apply(this, arguments);
func = null;
}
}
utilizzando:
var initializer= one( _ =>{
console.log('initializing')
})
initializer() // 'initializing'
initializer() // nop
initializer() // nop
Tentativo di utilizzare la funzione di sottolineatura "una volta":
var initialize = _.once(createApplication);
initialize();
initialize();
// Application is only created once.
Se stai usando Ramda, puoi usare la funzione "once" .
Una citazione dalla documentazione:
una volta Funzione (a… → b) → (a… → b) PARAMETRI Aggiunto nella v0.1.0
Accetta una funzione fn e restituisce una funzione che protegge l'invocazione di fn in modo tale che fn possa essere chiamata solo una volta, indipendentemente dal numero di volte che viene invocata la funzione restituita. Il primo valore calcolato viene restituito nelle chiamate successive.
var addOneOnce = R.once(x => x + 1);
addOneOnce(10); //=> 11
addOneOnce(addOneOnce(50)); //=> 11
mantenerlo il più semplice possibile
function sree(){
console.log('hey');
window.sree = _=>{};
}
Puoi vedere il risultato
this
posto diwindow
JQuery permette di chiamare la funzione una sola volta utilizzando il metodo 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>
Implementazione utilizzando il metodo JQuery su () :
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>
Implementazione utilizzando JS nativo:
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>
if (!window.doesThisOnce){
function myFunction() {
// do something
window.doesThisOnce = true;
};
};
Questo è utile per prevenire cicli infiniti (usando jQuery):
<script>
var doIt = true;
if(doIt){
// do stuff
$('body').html(String($('body').html()).replace("var doIt = true;",
"var doIt = false;"));
}
</script>
Se sei preoccupato per l'inquinamento dello spazio dei nomi, sostituisci una lunga stringa casuale per "doIt".
Aiuta a prevenire l'esecuzione appiccicosa
var done = false;
function doItOnce(func){
if(!done){
done = true;
func()
}
setTimeout(function(){
done = false;
},1000)
}