web-dev-qa-db-ja.com

一度だけ呼び出すことができるjavascriptの関数

一度だけ実行できる関数を作成する必要があります。最初の関数が実行された後は、そのたびに実行されません。私はC++とJavaから仕事を行うことができる静的変数について知っていますが、これを行うよりエレガントな方法があるかどうか知りたいですか?

94
vlio20

「実行されない」という意味が「複数回呼び出されても何もしない」ことを意味する場合、クロージャーを作成できます。

var something = (function() {
    var executed = false;
    return function() {
        if (!executed) {
            executed = true;
            // do something
        }
    };
})();

something(); // "do something" happens
something(); // nothing happens

@Vladloffe(現在削除済み)によるコメントへの回答:グローバル変数を使用すると、他のコードは「実行済み」フラグの値(選択した名前)をリセットできます。クロージャを使用すると、他のコードには偶然または故意にそれを行う方法がありません。

ここでの他の回答が指摘しているように、いくつかのライブラリ( nderscoreRamda など)には小さなユーティリティ関数(通常は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()という名前の関数があり、これはまったく異なることを行うことに注意してください。

188
Ted Hopp

再利用可能な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
}
58
I Hate Lazy

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;
    };
  };
25
asawyer

呼び出されたら、empty関数をポイントします。

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>
22
vsync

静的変数について言えば、これはクロージャーバリアントのようなものです。

var once = function() {
    if(once.done) return;
    console.log('Doing this once!');
    once.done = true;
};

once(); once(); 

その後、必要に応じて関数をリセットできます。

once.done = false;
9
Bunyk
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
}
4
Shmiddty

これを試して

var fun = (function() {
  var called = false;
  return function() {
    if (!called) {
      console.log("I  called");
      called = true;
    }
  }
})()
2
Database_Query

クロックフォードという名前の男から... :)

function once(func) {
    return function () {
        var f = func;
        func = null;
        return f.apply(
            this,
            arguments
        );
    };
}
2
Jowen

将来関数を再利用できるようにしたい場合、これは上記の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!
1
CMP

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)
}
1
orcaman

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);
    });
}

そして、それは一度だけ実行されます:)

1
Aldekein

初期設定:

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;
    };
}
1
andlrc

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
user300375

アンダースコア「1回」関数を使用しようとしています:

var initialize = _.once(createApplication);
initialize();
initialize();
// Application is only created once.

http://underscorejs.org/#once

0
Andrew Feng

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
0
David

あなたの問題を簡単に解決するシンプルなデコレータが付属しています

function one(func) {
  return function () {
     func && func.apply(this, arguments);
     func = null;
  }
}

使用して:

var initializer= one( _ =>{
      console.log('initializing')
  })

initializer() // 'initializing'
initializer() // nop
initializer() // nop
0
pery mimon

できるだけシンプルに

function sree(){
  console.log('hey');
  window.sree = _=>{};
}

結果を見ることができます

script result

0
Shreeketh K
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; */
0
RegarBoy