1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

javascript var と const

Posted at

#はじめに
去年、会社の若い人と、javascript のvar と const の違いについて話していた時に気が付いたことです。

image.png

ボタンをクリックすると、テキストボックスに数字が出るだけの簡単なjavascriptですが、下記のソースは動きません。

<script>
document.addEventListener("DOMContentLoaded", function(event) {

    for(var idx=1;;idx++) {
        var textId = "text" + idx ;
        var text = document.getElementById(textId) ;
        if(text==null)
            break ;
        var buttonId = "button" + idx ;
        var button = document.getElementById(buttonId) ;
        if(button==null)
            break ;
        button.onclick = function() {
            text.value = idx ;
        } 
    }
  });
</script>

ボタンが押された時には、

button.onclick = function() { text.value=idx; }

の text が nullになるためです。

var を constに変更する

以下は、正しく動作するソースです。

<script>
document.addEventListener("DOMContentLoaded", function(event) {

    for(var idx=1;;idx++) {
        var textId = "text" + idx ;
        const text = document.getElementById(textId) ;
        if(text==null)
            break ;
        var buttonId = "button" + idx ;
        const button = document.getElementById(buttonId) ;
        if(button==null)
            break ;
		const idx2 = idx ;
        button.onclick = function() {
            text.value = idx2 ;
        }
    }
  });
</script>

varをconstに変更することにより、クロージャ内の text と idx2 が意図したスコープ内の変数を参照するようになったためだと思います。

google closure compiler

varを使ったまま正しく動作する方法を調べるため、google closure compiler で変換してみました。

変換すると、以下のようになりました。

<script>

document.addEventListener("DOMContentLoaded",function(event) {
	var $jscomp$loop$0={} ;
	var idx=1;
	for(;;$jscomp$loop$0=
		{
		 $jscomp$loop$prop$text$1 : $jscomp$loop$0.$jscomp$loop$prop$text$1 ,
		 $jscomp$loop$prop$idx2$2 : $jscomp$loop$0.$jscomp$loop$prop$idx2$2
		} ,idx++) {
		var textId="text"+idx;
		$jscomp$loop$0.$jscomp$loop$prop$text$1=document.getElementById(textId);
		if($jscomp$loop$0.$jscomp$loop$prop$text$1==null)
			break;
		var buttonId="button"+idx;
		var button=document.getElementById(buttonId);
		if(button==null)
			break;
		$jscomp$loop$0.$jscomp$loop$prop$idx2$2=idx;
		button.onclick=function($jscomp$loop$0) {
			return function(){
				$jscomp$loop$0.$jscomp$loop$prop$text$1.value=$jscomp$loop$0.$jscomp$loop$prop$idx2$2
			}}($jscomp$loop$0)
		}
	}
);
 
</script>

ちょっと、読み取るのが難しいのですが、どうやら

button.onclick = function() {
    text.value = idx ;
}

button.onclick = function(text,idx) {
        
    return function() {
         text.value = idx ;
    }
}(text,idx) ;

と変更すると良いようです。

つまり、関数を呼び出すことで、呼び出された関数側でも独自のスコープを持つため、元の text,idxとは別のスコープの変数として認識されるようです。

#結論

javascriptのクロージャーのスコープは難しいと思いました。
javaはもう少し簡単ではないでしょうか?

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?