一度だけ実行できる関数を作成する必要があります。最初の関数が実行された後は、そのたびに実行されません。私はC++とJavaから仕事を行うことができる静的変数について知っていますが、これを行うよりエレガントな方法があるかどうか知りたいですか?
「実行されない」という意味が「複数回呼び出されても何もしない」ことを意味する場合、クロージャーを作成できます。
var something = (function() {
var executed = false;
return function() {
if (!executed) {
executed = true;
// do something
}
};
})();
something(); // "do something" happens
something(); // nothing happens
@Vladloffe(現在削除済み)によるコメントへの回答:グローバル変数を使用すると、他のコードは「実行済み」フラグの値(選択した名前)をリセットできます。クロージャを使用すると、他のコードには偶然または故意にそれを行う方法がありません。
ここでの他の回答が指摘しているように、いくつかのライブラリ( nderscore や Ramda など)には小さなユーティリティ関数(通常はonce()
という名前が付いています)[*])関数を引数として受け取り、返された関数が呼び出された回数に関係なく、指定された関数を1回だけ呼び出す別の関数を返します。また、返された関数は、指定された関数によって最初に返された値をキャッシュし、後続の呼び出しでその値を返します。
ただし、このようなサードパーティのライブラリを使用していないが、(上記で提供したノンスソリューションではなく)そのようなユーティリティ関数が必要な場合は、実装するのは簡単です。私が見た中で最も素晴らしいバージョンは これは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;
に変更したいと思います。 context
が呼び出されると、クロージャーがfn
への参照を維持する理由はありません。
[*]ただし、 this Drupal extension to 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
}
UnderscoreJsには、それを行う関数 nderscorejs.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;
};
};
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 once = function() {
if(once.done) return;
console.log('Doing this once!');
once.done = true;
};
once(); once();
その後、必要に応じて関数をリセットできます。
once.done = false;
var quit = false;
function something() {
if(quit) {
return;
}
quit = true;
... other code....
}
「自分自身を削除する」機能を単純に使用できます
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の要素が実行できない場合、1つ以上のA要素がある場合、関数を実行できません。
function Conditional(){
if (!<no elements from type A>) return;
// do stuff
}
これを試して
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
);
};
}
将来関数を再利用できるようにしたい場合、これは上記の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!
Node.jsを使用する場合、またはbrowserifyでJavaScriptを記述する場合は、 "once" npm module を考慮してください。
var once = require('once')
function load (file, cb) {
cb = once(cb)
loader.load('file')
loader.once('load', cb)
loader.once('error', cb)
}
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;
};
}
invalidate
と連携する再利用可能な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);
JSBinで試してみてください: https://jsbin.com/vicipar/edit?js,console
Bunykからの回答 のバリエーション
アンダースコア「1回」関数を使用しようとしています:
var initialize = _.once(createApplication);
initialize();
initialize();
// Application is only created once.
Ramdaを使用している場合は、関数 "once" を使用できます。
ドキュメントからの引用:
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 one(func) {
return function () {
func && func.apply(this, arguments);
func = null;
}
}
使用して:
var initializer= one( _ =>{
console.log('initializing')
})
initializer() // 'initializing'
initializer() // nop
initializer() // nop
できるだけシンプルに
function sree(){
console.log('hey');
window.sree = _=>{};
}
結果を見ることができます
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; */