『モテる JavaScript』フォローアップ – その 3 「使われているとなんかカッコ良い JavaScript の機能」


4 月 27 日(2013) に開催されました、第4回「ブラウザー勉強会」に『モテる JavaScript』というセッションのフォローアップ第三回目です。

前々回より以下のアジェンダでセッションの内容を紹介しています。

 

 

なお、当日使用したスライドは以下になります。

 

今回は 『使われているとなんかカッコ良い JavaScript の機能』 についてです。

 

使われているとなんかカッコ良い JavaScript の機能

なんてぇタイトルをつけておきながら、「カッコ良い」というのは甚だ各個人の主観に因るところが大きく、条件から尺度まで全国津々浦々各市町村老若男女人それぞれで、おそらく「格好良い」に正解はないと思いますので、ここでは、私が他人様のコードを見ていくうえで、効率的かつ簡潔、理にかなっており、なにより「カッコィィ…♥」と思った JavaScript の機能とコードを紹介させていただきます。

 

あいまいな評価の利用

JavaScript の構文の曖昧さは、他のプログラミング言語を学んだ人達から不評をかう 1 つですが、逆に言えばこの曖昧さを意のままに使いこなしてこそ真の JavaScript プログラマーと言えると思います。

たとえば、以下の値はすべて false と判断されます。

false
null
undefined
"" ( 空文字 )
0
NaN (not a number)

実行するまでもないですが、以下のコードを実行するとすべての結果に “偽” が返ります。

console.log(false?"真":"偽");
console.log(null?"真":"偽");
console.log(undefined?"真":"偽");
console.log(""?"真":"偽");
console.log(0?"真":"偽");

 

しかし、このあいまいな評価を利用することで、変数内の値が設定済か否かなどを簡潔に調べることができます。

たとえば、「変数に有効な文字列が設定されているか否か」を調べる場合、if(stringData !=””) とも if(stringData !=null) とも if(stringData !=undefined)  とも記述する必要はなく、 if(stringData) と記述するだけで、空文字以外の有効な値が設定されていると判断することができます。

 

if((stringData != ””)&&(stringData != null)&&(stringData != undefined)){

if(stringData){

 

同じく以下のような判断も可能です。

 

if(myObject)  //オブジェクトのインスタンスの有無
if(document.addEventListener) //メソッドの有無
if(items.length) //要素の有無 (数)

 

さらには || (OR) 演算子を使用することで、変数の中身が設定済みかどうかを判断し、設定されていなければ既定の値をセットするという処理が、以下のように簡潔に記述することができます。

 

//引数 stringArg に値が設定されていなければ、文字列 ”defaultValue” を設定
function(stringArg){
stringArg = stringArg||"defaultValue";

//安全な名前空間の登録
var myApp = myApp||{};

 

もちろん、この構文ですべての値について判断できるわけではないので、使用する値で有効かどうかきちんと確認してから使用するようにしましょう。

ちなみに、この真偽値に変換すると真(True)になることを 「truthy」 と言い、偽 (false) になる場合は 「falsy」 と言うそうです。格好良いので機会をみてぜひ使いたいところですね。

ただし、この曖昧な評価は、良いことだけには働きません。そもそも厳密性を欠いているわけなので正しく判断が行われない場合があります。

たとえば、以下のコードを実行すると括弧内の比較結果はすべて真であると判断され、コンソールには true が出力されます。

console.log(0 == );  //true
console.log(0 == ‘0’); //true
console.log(false == ‘0’); //true
console.log(null == undefined); //true
console.log(0 == ‘\t\r\n’); //true

 

こういった曖昧な比較を行わず、厳密な比較を行いたい場合は、文字通り厳密等価演算子 (===) と 厳密非等価演算子(!==) を使用します。

以下のコードでは厳密な比較が行われるため等価とは判断されず、false が返ります。

console.log(null === undefined); //false

また、パフォーマンス的にも一般的な Web ブラウザーでは厳密等価式のほうが高速に動作するので、あいまいな評価の機能を利用する理由がないかぎり厳密等価演算子を使用するようにしましょう。

 

 

即時実行関数

即時実行関数 (即時関数 or 即実行関数 とも言う) を使用すると、グローバルな名前空間に、関数名も、関数が使用している変数も公開することなく、複数ステップに渡る処理を定義すると同時に実行することができます。

具体的には以下のように記述します。

(function(){
   //関数内で行う処理
})();

 

即時実行関数は名前を持たない (匿名関数 or 無名関数) ため、一度の呼び出しにしか使用できませんが、名前空間を汚染することなく処理を終えることができるので、初期化などの処理に向いています。

名前がないこと、すぐに実行されること以外には通常の関数と違いはないので、以下のように引数を渡したり、

(function(arg1, arg2){
   var resultAdd = arg1 + arg2; //15 が格納される  
})(10, 5);

 

以下のようにメソッドチェーン (※後述します) 内部で定義した関数を呼び出すこともできます。

 

(function(){  
   this.init = function(){  
                  console.log("初期化処理");
                  return this;  
               };   
   this.load = function(){  
                  console.log("データのロード処理");  
               };   
   return this;
})().init().load(); //init 関数のあとに load 関数が実行される

 

さらに上記の関数は、以下のようにより簡潔に記述することもできます。

 

({
    init : function(){     
               console.log("初期化");
               return this;     
           },     
    load : function(){     
               console.log("データのロード処理");     
           }
}).init().load(); //init 関数のあとに load 関数が実行される

 

繰り返しになりますが、上記のような即時実行関数を使用すると、呼び出しは一回に限られますが、複数ステップにわたる処理、関数間にまたがる処理を、上位の名前空間を一切使用することなく実行することができます。

初期化以外の使いどころとしては、値を求めるための 1 度しか呼び出されないテンポラリの関数としての使い道もあります。

以下のコードではオブジェクト配列として返されたクレジットカードの情報を、カンマ区切りの文字列として変数 creditCard に格納しています。

var shopName = item.shopName; //店名
var avgFare =  item.avgFare;   //平均予算
var creditCard = (function(sepChar){ //使用できるクレジットカード名
           var dotList = "",
           cardList = item.creditCardList,
           length = cardList.length;
           for(var i=0; i<= length; i++){
              dotList += cardList[i].name + sepChar;
           }
           return dotList;
        })(",");

 

メモ : 即時実行関数の書き方について

即時実行関数は、(function(){//関数の処理})(); とも (function(){//関数の処理}()); とも(※)記述することができます。

(※)ようするに、引数を受け取るための括弧 () を関数全体を囲む括弧 () の外側に書くか、内側に書くかの違いです。

本によっては後者を推奨しているようですが、理由としては「括弧で全体をくくったほうがわかり易いから」という著者の主観的なものなので、私的にはどっちでも良いかな、と。ちなみに私は、後者だと引数を渡すときの記述や、内部のメソッドを呼び出す際の記述がなんとなく気持ち悪いので前者を使用しています。 Visual Studio 2012 が吐き出す Windows ストア アプリのテンプレートでも前者が使われています。

 

 

クロージャー

使いこなせるとワンランク上の JavaScript プログラマーになった気がする機能 No.1 (※個人的な感想です) な機能、クロージャー。

JavaScript の関数のロードと実行のタイミングの違いをうまく利用して、関数ロード時の値を保持することができます。

たとえば、以下の関数を実行すると、変数 add5、add20 に同じ関数を代入するにも関わらず、add5 に引数として与えた情報は、 add10 の引数には上書きされずに保持され、実行時には異なる値が返ります。

function makeAdder(x){
        return function(y){
        return x + y;   
    }
}

var add5 = makeAdder(5);
var add10 = makeAdder(10);
console.log(add5(2)); //7
console.log(add10(2)); //12

 

この内部の動作は一見、難しそうですが実際は単純な仕組みです。

関数にはメモリにロードされ実体 (インスタンス) を持つタイミングと、実行されるタイミングがあります。

makeAdder 関数の返り値は無名関数なので、変数 add5 に値がセットされた段階でメモリにロードされてインスタンスを持ちます。

このとき、無名関数は親(?) の関数の引数として渡された引数 x の値を持ったまま変数 add5 にセットされます。

その後に makeAdder 関数の引数として別の数字を与えて、他の変数 add10 にセットしたとしても、返り値として返される無名関数の実体は、新たに生成されたものなので、以前セットした変数 add5 の中身を書き換えることはありません。

上記のような動作となるため add5 と add10 を関数として実行した際にはそれぞれ異なる値が返るのです。

「セットされた値を保持する」という特性のためか、なぜか書籍で紹介されるクロージャーのサンプルは「関数が呼び出された回数をカウントする」といった、いまいち使いどころがわからないものが多いようです。

クロージャーは、それ自身と同じく、イベントハンドラのような、設定のタイミングと実行のタイミングの異なる関数で使用すると効果的です。

たとえば以下のコードは、リストにアイテムを追加し、リストがクリックされた際に、そのアイテムが追加された際の変数 i の値を alert で表示しようとしていますが、どのアイテムをクリックしても 6 が表示されてしまい、うまくいきません。

理由は、各リストアイテムに設定されたイベントハンドラが実行されるタイミングでは、すでに for 文のループか完了しており、変数 i の値は 6 になっているからです。

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
   <title></title>
<script type="text/javascript">
(function()
{
    onload = function() {

        var ctrl_listView = document.getElementById("itemList"),
        ctrl_item = null;
        for(var i=0; i <=5; i++){
            crtl_item = document.createElement("li");
            crtl_item.innerText = "リンク" + i;
            crtl_item.addEventListener("click",
                function() {
                    alert(i); //すべて6が表示される
                }, false);
           ctrl_listView.appendChild(crtl_item);
       }
    }

})()
</script>
</head>
<body>
   <ul id="itemList"></ul>
</body>
</html>

 

この問題の解決策の一つとして、リストアイテムを生成する際に setAttribute 関数を使用して、各リストアイテムの attribute に変数 i の値を入れておき、イベントハンドラ中で取り出すという方法も考えられます。

こういったわかり易くて純朴な方法は、私も嫌いではないのですが、わざわざ DOM を使用するという点において口の悪い第三者から「ダセェ、なにしてんだよ、このイモ〇郎w」 などと悪態をつかれる可能性があります。(あくまでも可能性の話をしています。)

クロージャーを使用して以下のように記述すれば、DOM や、情報を保持するための余計な変数を定義することなくこの問題を解決でき、心無い第三者から「イモ〇郎」呼ばわりされることもおそらくないでしょう。

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
   <title></title>
<script type="text/javascript">
(function()
{
    onload = function() {

        var ctrl_listView = document.getElementById("itemList"),
           ctrl_item = null;      
        for(var i=0; i <=5; i++){
            crtl_item = document.createElement("li");
            crtl_item.innerText = "リンク" + i;
            crtl_item.addEventListener("click",
                function(index) {
                    return function() {alert(index);}
                 }(i) //アイテム生成時の番号が表示される
            , false);
            ctrl_listView.appendChild(crtl_item);
         }
   }

})()
</script>
</head>
<body>
   <ul id="itemList"></ul>
</body>
</html>

 

上記のコードでは、各リストアイテムをクリックすると、期待した通り、リストアイテムを生成したときの番号が alert に表示されます。

このようにクロージャーはイベントハンドラーと組み合わせることでその特性をうまく使うことでできるうえ、JavaScript 初学者に「なんか凄そう」と思わせる効果もあるので、ぜひ使いどころを見つけて積極的に活用してみてください。

 

 

メソッドチェーン

JavaScript における一般的な関数の呼び出し方は、各関数の呼び出しを個別に記述しますが、返り値として返されたオブジェクト内の関数を次々と呼び出していくメソッドチェーンという呼び出し方があります。

imageメソッドチェーンを使用する理由は、ワンライナー(一行)で複数メソッドの呼び出しが可能であること、オブジェクトツリーの検索処理の軽減などが挙げられます。

とくに、前述したような無名関数内に定義した複数の関数を呼び出す際には、メソッドチェーンを使用すると便利です。

メソッドチェーンでの関数を呼び出しを可能にするには以下のようにオブジェクトを定義します。

var myPage = {
   methodOne : function(){
       console.log("ONE");
       return this;   
    },
   methodTwo : function(){
       console.log("TWO");
       return this;
    },
   methodThree : function(){
       console.log("THREE");
          return this;
    }
}

 

呼び出し方は以下のようになります。

myPage.methodOne().methodTwo().methodThree();

なお、前のメソッドのメソッドの結果にアクセスするには、以下のようにメソッドの結果を受けとるためのプロパティを用意しておき、受け渡しに使用するなどの方法があります。

var myPage = {     
   resultValue : “”,    
   methodOne : function(){
       console.log("ONE");
       this.resultValue = “処理結果”;
       return this;
    },
   methodTwo : function(){
       console.log("TWO");
       console.log(this.resultValue);
       return this;
    },
   methodThree : function(){
       console.log("THREE");     
        return this;
    }
}

 

メソッドチェーンはワンライナー (一行) で複数メソッドの呼び出しが記述できるだけでなく、オブジェクトをそのまま次のメソッドに渡すことができるので、引数の数を減らす、名前空間の使用を減らせるなどのメリットもあります。

また、工夫しだいではいろいろ使い方がありますので、ツウを唸らす恰好良い使い方をぜひ考えてみてください。

 

まとめ

今回は、「使われているとなんかカッコ良い」 (と、私が思っている) JavaScript の機能について紹介してきました。

紹介した内容は以下の 2 点に要約できるでしょう。

  • JavaScript の良い意味での「曖昧さ」を理解してうまく使うことでコンパクトで効率の良いコードが書ける
  • 関数の実行タイミング、メモリの使われ方をすることで頓智の効いたコードが書ける

TypeScriptCoffeeScript を使用して、危なげない JavaScript のコードを “出力” しても良いでしょうが、JavaScript が好きならば、JavaScript より深く学んで、読んだ人が思わずニンマリとする恰好良いコードを書きましょう。

なにより、コードを書くことを楽しんで!

enjoy!

<参考>

O’RELLY Japan JavaScript
http://www.oreilly.co.jp/books/9784873113296/

O’RELLY Japan JavaScript: The Good Parts――「良いパーツ」によるベストプラクティス
http://www.oreilly.co.jp/books/9784873113913/

O’RELLY Japan JavaScriptパターン――優れたアプリケーションのための作法
http://www.oreilly.co.jp/books/9784873114880/

 

<おしらせ>

私が監修のお手伝いをさせたいただきました HTTP ネットワークキャプチャツール Fiddler の解説本が、日本 O’RELLY さんから発売になります。

こちらもよろしくお願いいたします。

Real Time Analytics

Clicky

Comments (1)

  1. a より:

    モテるためにクロージャーを無理して使います!

    何となくカッコイイの最高

Skip to main content