2011/05/24


部下にも何度も説明してて、この辺がC言語のポインタみたいな鬼門なのかなーとか思いながら。
javascriptでhtml内のonclickの内容を書き換えようとしています.. - 人力検索はてな

javascriptでhtml内のonclickの内容を書き換えようとしていますが 変更後の関数に変数を渡すと、変更後の関数(load)自体が実行されてしまい、うまくいきません 現在は以下のように、onclickの内容を変更しようとしていますが、 関数を実行させずに、html内のonclickの内容だけ書き換える場合はどのようにしたらいいですか?

document.getElementById('box').onclick = (function(id){ load(id) })(userid);

http://q.hatena.ne.jp/1305849029
こういうコードになった経緯を考えると、その間違いがなぜ起こったかわかりやすいし、人力検索で回答貰った後で再度間違う事も少ないと思う。

おそらくだが、この人はこのコールバック関数登録をループかなんかの中で行おうとしたんじゃないかと(勝手ながら)思った。 <script>
window.onload = function() {
    var elem = document.getElementsByTagName('a');
    for (var n = 0; n < elem.length; n++) {
        elem[n].onclick = function() {
            alert(n);
        }
    }
}
</script>
<body>
<a href="#">foo1</a>
<a href="#">foo2</a>
<a href="#">foo3</a>
</body>
このHTMLでfoo1,foo2,foo3をクリックするとどうなるだろう。0,1,2と答えた人は、この質問者と同じ間違いを起こすだろう。答えは全て3が表示される。
これが何故起きて、どう解決すべきかが分かると今後の理解も早いと思う。

ループのインデックスであるnは、コールバック関数から見ると外のスコープにある。つまりコールバック関数が呼び出された時にはループは3回回ってしまっていて、結果どれをクリックしても3が表示される。
じゃぁどうするか。
var elem = document.getElementsByTagName('a');
for (var n = 0; n < elem.length; n++) {
    var f = n
    elem[n].onclick = function() {
        alert(f);
    }
}
こうじゃない?って答えた人は×。それ何も変わってないから、どれクリックしてもまた3だよって答えた人も×。答えは全て2。
なぜ前回は3が表示されたのか。それはforループが回りきってループを外れる条件に達した、つまり3になったから。ではなぜ今回は2なのか。ループは0,1,2の時にしか実行されなかったから。
わかりますよね。

じゃぁこれ、どうやって個々に0,1,2の値を表示させるのよ...となる。答えは聞いてない!答えは複数ある。
人によってはeval()使っちゃう人もいるだろうし、昔のコードでは結構見た。未だにそんなコードの保守をやらされる場合もある。出来るだけモダンな書き方したいですよね。
問題はスコープでしたよね。毎回同じスコープが実行されちゃってるのが問題で、コールバック関数から見ると全て同じ変数を見てしまっているのが問題。
そこでループの度にスコープを作ってあげるのです。以下の様なコードを見ることが多いと思います。 (function() {
    // ...
})()
関数を文として扱わず、式として呼び出しているんですよね。最近はこんな書き方もある様です。
+function() {
    // ...
}()
これで新しいスコープが出来ます。でもこれだけでは解決しません。この作ったスコープで変動しない値を保持してあげます。
var elem = document.getElementsByTagName('a');
for (var n = 0; n < elem.length; n++) {
    (function() { // ココが決め手
        var f = n
        elem[n].onclick = function() {
            alert(f);
        }
    })() // ココが決め手
}
変数fにn渡してます。このループが3回実行されると3回スコープが作られ、それぞれにfというスコープ内変数が宣言されます。これにより個々のイベントハンドラから0,1,2が参照出来る様になるという事です。
ちなみにvar宣言したくない人はよく for (var n = 0; n < elem.length; n++) {
    (function(n) {
        elem[n].onclick = function() {
            alert(n);
        }
    })(n)
}
こう書いたりもします。
今回の問題、よくカウントダウンタイマを作ろうとして for (var n = 0; n < 10; n++) {
    setTimeout(function() {
        document.getElementById('timer').innerHTML = '残り' + (10 - n) + '秒'
    }, 1000 * n)
}
こう書いちゃう人もいます。理屈は同じですよね。気をつけて。
Posted at by



2011/05/20


vimでsleep sortだなんて!
おれたちにできない事を平然とやってのけるッ!そこにシビれる!あこがれるゥ

注意
  • remote機能が必要です。
  • ソートに失敗するとvimがたくさん起動します。
  • たまに失敗します。
  • ネタです。ご理解下さい。
functions:getsid()
  return matchstr(expand('<sfile>')'<SNR>\zs\d\+\ze_getsid$')
endfunction
let s:sid = s:getsid()

functions:add(v)
  echon a:v.' '
endfunction

let s:arr = [5,3,6,3,6,3,1,4,7]
for i in s:arr
  if has('win32') || has('win64')
    silentexe '!start /min '.v:progname.' -u NONE --noplugin --cmd '.shellescape("sleep ".(i*100)."m|call remote_expr('".v:servername."','<SNR>".s:sid."_add(".i.")')|q")
  else
    silentexe '!'.v:progname.' -u NONE --noplugin --cmd '.shellescape("sleep ".(i*2)."|call remote_expr('".v:servername."','<SNR>".s:sid."_add(".i.")')|q").' &'
  endif
endfor
結果
1 3 3 3 4 5 6 6 7
Posted at by




4chan BBS - Genius sorting algorithm: Sleep sort
http://dis.4chan.org/read/prog/1295544154
常識を覆すソートアルゴリズム!その名も"sleep sort"! - Islands in the byte stream
http://d.hatena.ne.jp/gfx/20110519/1305810786
Sleep sort with AnyEvent - TokuLog 改メ tokuhirom’s blog
http://d.hatena.ne.jp/tokuhirom/20110519/1305814594
こういうのはGoが得意分野だよね。 package main

import (
    "os"
    "strconv"
    "syscall"
)

func main() {
    args := os.Args[1:]

    done := make(chan int64)
    for _, arg := range args {
        i, _ := strconv.Atoi64(arg)
        go func(i int64) {
            syscall.Sleep(i * 1e9)
            done <- i
        }(i)
    }
    for _ = range args {
        println(<-done)
    }
}
Posted at by