Help us understand the problem. What is going on with this article?

javascript var と const

はじめに

去年、会社の若い人と、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 で変換してみました。

https://closure-compiler.appspot.com/home

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

<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はもう少し簡単ではないでしょうか?

old-pg
50代のプログラマーです。 Web制作会社で、主に、javaとphpのプログラムを書いています。 発見したことの備忘録です。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした