40歳からのキャリアチェンジ

20代はエンジニア・PM、30代はWeb系エンジニア向けのキャリアアドバイザー。40代の今はフリーランスで開発含めて色々やってます。技術ネタとしてはRuby/RailsとJavaScript関連あたり

非エンジニアな人におくるJavaScriptの基礎:(3)変数について

前回書いたエントリに関してid:donayama さん のこのはてブコメントで

forの中のvarは危険な香り

というフィードバックをいただき、さらに@ryugoo_さんからもTwitterでこんな感じで言及してもらいました

最近、CoffeeScriptで書いてて、素のJavaScript書いてなかったのと、ひな形となるアプリをベースに、コピペしながらコード書いてたから・・と書く単なる言い訳でしかないですが、いづれにしても書き方としてマズい所があったので、制御文のifを取り上げる前に、変数について今回説明することにします。

目次


グローバル変数とローカル変数

ひとまずこれまで書いていたサンプルを一旦削除して、app.jsにこんなコードを書いたとします

var globalMessage = 'Global Hello';
alert(globalMessage);  // (1)
showMessage(globalMessage);  // (2)
alert(message);  // (3)
function showMessage(text){
    var message = 'Hello';
    alert(message);
    alert(text);
    alert(globalMessage);
}

(1)から(3)の処理はそれぞれ以下のようになります

  1. alert()の結果として Global Hello が表示される
  2. 引数にglobalMessageが渡されてshowMessage()が実行される。showMessageの中ではalertが3つあり、最初に関数内で宣言された変数のmessageに代入されてる Hello が表示される。その次に引数textで渡された Global Hello が表示される。最後にグローバル変数として宣言された globalMessageに代入されてるGlobal Helloが表示される。
  3. 何も表示されない

グローバル変数とローカル変数のイメージ

グローバル変数とローカル変数のイメージとしてはこういう形です

グローバル変数

f:id:h5y1m141:20140328082030p:plain

ローカル変数

f:id:h5y1m141:20140328082657p:plain

ローバル変数は潜在的にどこかで変更される可能性があり、またプログラムの一部はそれに依存してしまう恐れがあるからである。グローバル変数はそれゆえ相互依存を生み出す無限の可能性を持っており、相互依存が高まることは複雑性を増大することにつながる。(ウィキペディアより引用)

という一文があります。上記記載したサンプル程度のコードなら、すぐに把握できるので大きな問題がないですが、それなりの規模のアプリケーションになると、使ってる変数が増えてきて、それがグローバルな変数だったりすると、上記引用したような問題が生じるかと思うので、必要最低限に抑えたほうが良いかと思います。

JavaScriptの変数のスコープ

先ほどまで、ローカル変数とグローバル変数について触れました

この話を踏まえて、先日私が書いたサンプルコードの一部を取り上げながら少し踏み込んで説明します

for(var i = 1; i < 6; i++){
    var tab = createTabElement(i + '!!!');
    tabGroup.addTab(tab);   
}

そもそもブロックスコープがない

このforループでのブロック内で、変数tabを宣言してます。これは指摘されるまで全く気づかなかったのですが関連しそうな情報をググっていたら

Java などの言語では、if や for などの {} で囲まれたブロックごとにもブロックスコープがありますが、JavaScript には存在しません。 JavaScript でどうしてもブロックスコープを使いたい場合は、with 命令を使う方法や、無名関数を定義と同時に呼び出すなどの方法で、擬似的にブロックスコープを作ることは可能です。 JavaScript のスコープを理解する より

というのを見つけました。なので、上記コードは

var tab;
for(var i = 1; i < 6; i++){
    tab = createTabElement(i + '!!!');
    tabGroup.addTab(tab);   
}

というのと同じです。

ついでに@ryugoo_さんからもこんな感じで教えてもらいましたが、forループ内のカウンターとして利用してる変数iもループ外で宣言するのと同じとのこと。

前回書いたサンプルコードは以下のようにしておくべきでしたね ^^;

// 最初に変数宣言をまとめて行う。複数ある場合には 変数名の後カンマで区切れば続けて記述することが可能
var i,tab,tabGroup = Titanium.UI.createTabGroup();
Titanium.UI.setBackgroundColor('#000');
for(i = 1; i < 6; i++){  //(1)
    tab = createTabElement(i + '!!!'); // (2)
    tabGroup.addTab(tab);   
}
tabGroup.open();

function createTabElement(titleNumber){
    // 関数内の先頭箇所で変数宣言をまとめて行う。
    var win,label,tab;
    win = Titanium.UI.createWindow({  
        title:"Tab" + titleNumber,
        backgroundColor:'#fff'
    });

    label = Titanium.UI.createLabel({
        color:'#999',
        text:'I am Window' + titleNumber,
        font:{fontSize:20,fontFamily:'Helvetica Neue'},
        textAlign:'center',
        width:'auto'
    });

    win.add(label);

    tab = Titanium.UI.createTab({  
        icon:'KS_nav_views.png',
        title:"Tab" + titleNumber,
        window:win
    });

    return tab;

};

最後に

上記のスコープについて調べてる中で、変数の巻き上げ(ホイスティング)も関連してきそうということで、そこについても文章を書きかけたのですが、非エンジニアな人におくるJavaScriptの基礎というテーマからすると、スコープが広がりすぎてしまうので、そこについて言及するのは止めておきました

もしも興味持たれた方がいましたら、調べてる中で以下3つが個人的に参考になったのでチェックしてみてください

非エンジニアな人におくるJavaScriptの基礎記事一覧