2006年10月27日

こんなのもラク?遅延/繰返し/回数指定して function を複製する2

javascriptで。
・ 指定回数繰り返しさせる関数
・ true の間、延々と繰り返しさせる関数(カウンター付)

を前回の、「遅延させるだけの関数」から作成します。

「3歩または5歩進んで止まる」を100回繰り返します。

・ 指定回数繰り返しさせる関数
・ 角度をずらす関数
を使ってわずか2行で仕上がります。



■ 動作確認
IE 4.0/5.0/5.5/6.0
FF 1.0/1.5
NS 6.2/7.1
OP 7.02/8.53/9.01
Sleipnir 2.30
今回でほぼ最終形です。



指定回数繰り返しさせる関数

function times(fn, tm, cnt){
  var id = Math.random() + "-" + (new Date()).getTime();
  var cb = function (){
    var slf = this, arg = arguments;
    if(!slf[id]){slf = {self:slf}; slf[id] = 0;}
    slf[id]++;
    var tid = setTimeout(function (){
      clearTimeout(tid);
      fn.apply(slf.self, arg);
      if(slf[id] < cnt) cb.apply(slf, arg);
    }, tm);
  }
  return cb;
}

今回からは、利用価値が上がると思いますので、ちゃんと clearTimeout を使っていきます。また、このコードでは setTimeout を使ってますが、上のサンプルでは setInterval を使ってます。(こっちのが好きなのでw)

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

ローカルで定義した新しい関数(cb)/繰り返したい元関数(fn1)と同一の関数の2つを setTimeout で呼び出しています。
ただ、カウンター(何回呼び出されたのか?)を保持させる変数は、グローバルにしたくないので、cb を呼び出すときに this に加えて一緒に渡してやっています。

※ this は持ち回されるため、カウンターの保持に利用しています(times 関数内でのみです。他への影響はないと考えていいと思います)。ただ、何の対策もないと times(times(func, 2000, 2), 7000, 4) などのような場合にカウンターの競合が起こって誤動作します。そのため、カウンターを保持するための識別子(コード中では id)は、17桁のランダムな数値と現在時間(msec)を使っています。
※ fn.apply(slf.self, arg); を var tid = setTimeout(function (){ の前に出すと、初回1回目は遅延せずに実行されます。上記サンプルは、そうなっています。
※ 上のコードの場合、setTimeout と setInterval は相互互換です。好きな方を使ってください。setTimeout はズレが生じることがありましたが、現在は未調査です。


■ サンプルに関して

上記サンプルのURLはこちら
右クリック --> ソースの表示や、ファイル --> 名前をつけて保存で取得してください。

サンプルでは4種の「動き」が必要になります。クラスを作成したり、1つの柔軟で強力な関数を作ったりと色々考えられます。
times() は、指定時間、指定回数繰り返しさせるようにして複製するので、今回のように規則的な動作の場合は、はるかにラクにできます。

サンプルでは、times() のほか move()(角度をずらす関数)があるだけで、
  times(times(move, 100, 3), 1000, 100)(対象object1);
  times(times(move, 150, 5), 1000, 100)(対象object2);
の2行で処理しています。結局これは・・・

var obj1move = times(move, 100, 3);
var obj2move = times(move, 150, 5);
var obj1round = times(obj1move, 1000, 100)
var obj2round = times(obj2move, 1000, 100)

のように4つの関数に複製させたのち、実行するのと等価です。

true の間、延々と繰り返しさせる関数(カウンター付)


function ticker(fn, tm){
  var id = Math.random() + "-" + (new Date()).getTime();
  var cb = function (){
    var slf = this, arg = arguments;
    if(!slf[id]){slf = {self:slf}; slf[id] = 0;}
    slf[id]++;
    var tid = setTimeout(function (){
      clearTimeout(tid);
      if(fn.apply({self:slf.self, count:slf[id]}, arg) == true)
        cb.apply(slf, arg);
    }, tm);
  }
  return cb;
}

使用例: fn2 = ticker(fn1, tm);
関数 fn1 の複製が true を返す限り、tm 時間(msec)ごとに、関数 fn1 の複製が実行される関数が、fn2 に返されます。

「指定回数繰り返しさせる関数」(times)と考え方は、まったく同じです。
times では、ローカルで新規に定義した関数(cb)がカウントして、指定回数にまで繰り返していましたが、ticker では、this を用いて、関数 fn1 の複製へも呼び出し回数を報告しています。
こちらも ticker(ticker(func, 2000), 7000); と入れ子にしても問題ありません。

■ テスト
function alerm(val){
  if(this.self != window) var slf = this.self; else var slf = "";
  alert(slf + val + "ごと" + this.count + "回目");
  if(this.count < 5) return true;
}

上記関数があるとして。
var newFunc = ticker(alerm, 2000);
newFunc.call("this を渡して", "2秒");

とすれば試せます。call や apply が分からない人は、newFunc("2秒"); でも実行できますので、試してみてください。
2秒おきに5回、アラートが起動します。
要点としては、カウンタは、呼び出される関数内で this.count が保持しているということです。そうなると、今までの関数と違って注意する点は、this が汚れているということです。
※ 通常の this は this.self で取得できます。
※ 汚れるといっても元のオブジェクトは無傷です。
※ if(fn.apply・・・ の1行を var tid = setTimeout(function (){ の前に出すと、初回1回目は遅延せずに実行されます。
※ 上のコードの場合、setTimeout と setInterval は相互互換です。

なぜ this なのか?・・・もう当然というか^^
・ this が汚れて困るのは上級者だけ。つまりなんとでも対処できるハズw
・ arguments や、そのためだけのラッピングを行うより断然ラク!
・ this.self にしても this.count にしても言葉的に分かりやすい。^^


■ サンプルに関して

上記サンプルのURLはこちら
右クリック --> ソースの表示や、ファイル --> 名前をつけて保存で取得してください。

こちらの ticker は、逆に true が返ってきたら止まるようにして変形させてます。if(!fn.app・・・ の ! を付けただけ。サンプルではこちらの方がラクなので・・・てかその方が一般的にも使いやすいかも。

3匹のイモムシ、種類や個性があるのですが、分かります?イモムシ1匹にシャクトリムシ2匹。シャクトリムシは、猪突猛進タイプときょろきょろ迷うタイプかな。あんまヤリすぎると虫らしくないのでちょいと微妙で止めといた。
クラス定義してインスタンスにしても洗練されたコードで出来ますが、こちらはこちらでかなり簡潔。各関数以外、グローバル汚して無いのもいいかな。

ちなみに説明は無しです。ちょっと大変なのでw 分からないところを言って頂ければお答えします。
posted by HiFa at 17:03 | 🌁 | Comment(3) | TrackBack(0) | JavaScript雑感 | このブログの読者になる | 更新情報をチェックする
>>> スパムコメントは消してますよん。 お互い無駄な労力は避けましょう。 <<<

この記事へのコメント

リアルなのに短いな。なぜこんなコードでここまで出来るんだ?
全然理解できん。
Posted by 通りすがり at 2006年11月09日 22:57
うわ、ホントに短い。ネスケでもちゃんと見える。ちょっとうれしいかも o(^v^)o
Posted by おちょぼ at 2006年11月09日 23:20
初めてスパムじゃないコメント頂きました。ちょっと感激。

>通りすがりさん

おっしゃって頂ければ回答しやすよ。

>おちょぼさん

ま、基本的なものしか使ってないので。

実際問題短けりゃ良いというものでもなかったりします。
チームで開発する場合は、分かりやすさ、読みやすさは重要かと。
ま、ウチは零細なので、チーム開発できないんで好き放題やってますがw

通りすがりさんのおっしゃってるのは、おそらくこんな意味が含まれる?
Posted by HiFa at 2006年11月10日 09:41
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

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


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

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

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