2006年10月25日

遅延/繰返し/回数指定して function を複製する1

ある関数を繰り返しさせる関数のほか、使途不明の逐次指定型の遅延関数?です。

最近、setInterval に関係したネタを2つほどUPしたので、今度は setTimeout を利用して、任意の function を遅延や繰り返しなどする関数に変形・複製する関数を書いてみました。



なんですが、ちょっと長くなったので。

・ 遅延させるだけの関数
・ true の間、延々と繰り返しさせる関数
・ 指定回数繰り返しさせる関数
・ true の間、延々と繰り返しさせる関数(カウンター付)
・ 逐一指定して遅延させる関数

と5つを2,2,1と分けてUPします。
てことで今回は、2つ。



 まずは遅延させるだけの関数

function delay(fn, tm){
  return function (){
    var slf = this, arg = arguments;
    setTimeout(function (){fn.apply(slf, arg);}, tm);
  }
}


使用例: fn2 = delay(fn1, tm);
fn2 に、tm 時間(msec)後に関数 fn1 と同じ動作をする関数が返されます。

遅延させるだけなら setTimeout(function (){alert("2秒");}, 2000); なんぞで十分なのですが、これを元にして色々作っていくので・・・、ま、おいといて。
function alerm(val){alert(val);} のような関数があるとします。

repAlerm = delay(alerm, 2000); // 2秒後に起動する関数として複製
repAlerm("2秒"); // 実行


としてやると、alerm が遅延する関数 repAlerm として複製(と呼んでいいんだろうか・・)&実行されます。setTimeout と同じような用法ですが、実行するのではなく関数を戻り値とします。さらに、新しい関数(repAlerm)は、もとの関数(alerm)に影響されません。試しに、上2行の間に alerm = null; を挿入しても起動します。

複製の利点としては、色々な遅延時間で束縛した関数が作成できるってことですが、複製ではなく、あらかじめ組み込まれているメソッドとしてしまうなんてクールなこともできます。Function.prototypeを拡張して遅延実行を実現する(最速インターフェース研究会さん)

※ 上の例では delay(alerm, 2000)("2秒"); とすれば1行で複製と実行ができます。
※ ユーザ定義の関数ならOKですが、alert の様に apply が組み込まれていない関数には使えません。上の例のようにラップしてやる必要があります。
※ fn = delay("".concat, 5000); など、ちゃんと遅延された関数が fn に代入されますが無意味な関数です。^^);
※ val = delay("".concat, 5000).call("1+2は", "3"); も、当然無意味です。^^);;;
※ apply やら call なんて使わないよーって場合は、slf = this, は不要です。apply の slf は、null とでもしてください。

 true を返す限り、延々と繰り返して起動する関数

元の関数が true を返す限り延々と繰り返して起動する関数として複製を行います。
function repeat(fn, tm){
  var cb = function (){
    var slf = this, arg = arguments;
    setTimeout(function (){if(fn.apply(slf, arg)) cb.apply(slf, arg);}, tm);
  }
  return cb;
}

遅延させるだけの関数では、名無しの関数を直接 return に記述してましたが、こちらは一旦ローカルな変数に代入しています。
そして、setTimeout で再度自分自身を呼び出してやることで、一定時間ごとに繰り返すようにしています。

使用例: fn2 = repeat(fn1, tm);
fn2 に、tm 時間(msec)ごとに関数 fn1 と同じ動作を繰り返す関数が返されます。用法は上と同じw
ただし、繰り返しを止めさせる手段がないとちょっと使えないので、fn1 が true を返す場合に限り繰り返します。
ま、こちらも遅延するだけの関数と同じく、自分自身を setTimeout で呼び出せば済むので、明確な複製の意図がなければ持ち腐れですw

if(fn.apply(slf, arg)) cb.apply(slf, arg); を
if(fn.apply(slf, arg) != 'stop') cb.apply(slf, arg);
などと書き換えれば、'stop' との文字列が返ってきた場合にだけ繰り返しを止めるなどのようにもできます。また、if(fn.apply(slf, arg)) を setTimeout の前に出すと、最初1回目は遅延されずにただちに実行される関数になります。

サンプル
num = 1;
function fn1(){
  num = num / 10;
  var r = 1.0 / num;
  alert(r);
  if(r < 100000) return true;
}
fn2 = repeat(fn1, 1000);
fn2();

グローバルな変数 num があるので、あまりいい例ではないのですが・・・しかも1秒ごとに呼び出す意味もないw 100000未満の間、10倍されて行きます。

ちなみにIEだと、99999.99999999998ってなりますが、バグじゃありません。firefox は、16桁目が 8 じゃなく 9 。
alert(1 / 0.00001);
alert(1 * 100000);
は、両方とも10万倍しているだけですけど、違う結果になります。試してみましょー^^; ちなみに、こんなところでなんだけど。関係ない話しだし。まぁいいか。
(1/3) + (1/3) + (1/3) = 1 ですよね。なら・・・
0.33333・・・ + 0.33333・・・ + 0.33333・・・ は、 0.99999・・・でOK?
1/3 = 0.33333・・・なのに?なんてのもw
posted by HiFa at 16:52 | 🌁 | Comment(0) | TrackBack(0) | JavaScript雑感 | このブログの読者になる | 更新情報をチェックする
>>> スパムコメントは消してますよん。 お互い無駄な労力は避けましょう。 <<<

この記事へのコメント

コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
この記事へのトラックバックURL
http://blog.seesaa.jp/tb/26143018
※言及リンクのないトラックバックは受信されません。

この記事へのトラックバック
×

この広告は1年以上新しい記事の投稿がないブログに表示されております。