8/21 JavaScript : The Good Parts の読書会に参加しました。
読書会は初めての参加です。

読書会とは
特定の本を各自持ち寄り内容を読み進めていき、わからないところや、おもしろいところを共有しようというもの。

主催はTwitter@nine0527さん。

今回はJavascript本の良書 The Good Parts の1章~3章でした。

やはり、読み進めていくうちにその場ではわからないことも多々有り。 所々については宿題ということになったわけです。

全体の議事録はnine0527さんにお任せするとして・・・

私は気になったところをピックアップ。 序盤ということもあって3章に疑問が集中しました。

3.1 オブジェクトリテラル

・プロパティ(連想配列)に関数を格納できる!

Javascriptにおけるオブジェクトはキーによって整理された変更可能なデータの集合体。
関数や正規表現もオブジェクトであるとのこと。

つまり、こんな書き方が可能。


var inoki = 
    {
        one  : "イチ",
        two  : "ニー",
        three: "サン",

        //プロパティに関数を格納
        call: function( n )
        {
            if ( typeof n != "number" )   n = 1;
            for ( i = 0; i < n; i++ )
            {
                alert ( this.one + "! " + this.two + "! " + this.three + "! " + "ダー!!" );
            }
        }
    };

inoki.call();      //イチ! ニー! サン! ダー!!

デモボタン

熱いですね。

全く知りませんでした。 実は、オブジェクト生成後もプロパティを自由に定義して値を格納できることすら、今回初めて知りました(笑)

3.5 プロトタイプ

・prototypeってなんぞ。

Javascriptを勉強する際に大きな壁となるprototypeってなんやねん、と。

本の中では委譲って書いてあります。 継承っぽい感じもしますが、すこし違和感があります。 読書会では、大体の概念は共有できたように思います。

ここでは概念ではなく動作から検証することに。

本のサンプルでは、Object.create関数を作っています。


//createの実装確認
if ( typeof Object.create !== 'function' )
{
    //create関数の実装
    Object.create = function(o)
    {
        //空のオブジェクトを生成
        var F = function(){};

        //Fのprototype参照は引数oとする。
        F.prototype = o;

        //オブジェクトFを返す。           
        return new F();
    };
}

最初、このcreate関数自体の意味がさっぱりでした。 この関数は引数に渡したオブジェクトをprototype(元型)とする新しいオブジェクトを生成します。

例によって車で考えてみます。 (車で例えてしまうと継承っぽくなってしましますが)

まずcarオブジェクトを作ります。


//carオブジェクトを生成
var car = 
{
    type : "CAR",
    name : "Corolla",
    engine : "Normal",
    fuel : 100,
    maxSpeed : 180
};

次にcarオブジェクトをプロトタイプとするsportCarオブジェクトを生成


//sportCarオブジェクトを生成
var sportCar = Object.create(car);

//sportCarオブジェクトのプロパティに値をセット
sportCar.name = "RX-7";
sportCar.engine = "Rotary";
sportCar.fuel = 50;
sportCar.maxSpeed = 250;
sportCar.wing = "GTwing";

ここで次のように置き換えて実際の値を見てみましょう。

  • 『sportCarオブジェクトはcarオブジェクトを一方向参照する。』
置き換えるとこんな感じ
sportCar.prototype = car
sportCarオブジェクト
参照方向
carオブジェクト
 
type : "CAR"
name : "RX-7" name : "Corolla"
engine : "Rotary" engine : "Normal"
fuel : 50 fuel : 100
maxSpeed : 250 maxSpeed : 180
wing : "GTwing"  

alert( car.maxSpeed );        //① car.maxSpeed → 180
alert( car.wing );            //② car.wing → undefined
carオブジェクト参照は表の右側から ①car.maxSpeed : 180 特に難しいことはない。 ②car.wing : (undefined) carオブジェクトには存在しない。(ただし、car.prottype.wingをたどることに注意)

alert( sportCar.engine );    //③ sportCar.engjine → "Rotary " 
alert( sportCar.type );        //④ sportCar.type → car.type → "CAR"
sportCarオブジェクト参照は表の左側から ③sportCar.engine : "Rotary" 我らがロータリーo(・∀・)oブンブン ④sportCar.type : "Car" プロパティは存在しないのでプロトタイプを辿りcar.typeを参照 ⑤sportCar.turbo : (undefined) carオブジェクトにも存在しない。(ただし、car.prottype.turboをたどることに注意) じゃぁ、これは???

car.sticker = "k5m-labs";

alert( car.sticker );            //car.sticker → ???
alert( sportCar.sticker );        //sportCar.sticker  → ???   
もうわかりますよね。
プロトタイプチェーン
→ をたどって(prototype参照して)プロパティを参照することをプロトタイプチェーンといいます。 また、プロトタイプチェーンは参照時のみ実行される。 代入やプロパティ削除のdelete実行ではプロトタイプチェーンは発動しない。

3.7  プロパティの列挙

・配列だとlengthプロパティまでたどる?
配列のプロパティを列挙すると、Array.prototype.lengthまでたどるのかどうか? という話になりました。 Javascriptではfor in ループでプロパティ列挙ができるとのことで実験。

var arrays = new Array( "one", "two", "three", "four", "five");
alert( arrays.length );        //5

for( var value in arrays )
{
    document.write(value + ";" );
}

//出力
//0;1;2;3;4;     
あれ?lengthプロパティが列挙されていない?? そのほかのtoStringなんかも出力されない。 調べてみると、lengthやtoStringなどはDontEnum属性であると。
DontEnum属性
DontEnum属性が付与されたプロパティは、for…inループによる列挙の対象から外される。
Internet Explorerの仕様として、DontEnum属性にはこんな例も。 toStringなどのDontEnum属性を新たに生成したオブジェクトのプロパティとして設定すると、DontEnum属性をも引き継いでしまう。

var obj = new Object();

obj.hoge = "fuga";
obj.toString = function ()
{
    return "これはobj.toString関数の返値です。";
}

alert ( obj.toString() );
obj.hogeとobj.toStringが追加実装されている。
出力される値
これはobj.toString関数の返値です。

var properties = "";
for( var property in obj )
{
    properties += property + ";";
}

alert( properties );
これはブラウザによって出力される値が違う。
IE8
hoge;
firefox3
hoge;toString;

obj.toStringがDontEnum属性をも引き継いでいることが確認できる。

その他、配列などの値列挙の為にfor inを使うとあまり良くない状況になることもあるらしい。 非常にレアケースではあるけど、配列のprototypeであるArrayのprototypeに独自拡張を加える状況など。


var arrays = new Array( "one", "two", "three", "four", "five" );

//値列挙
var output1 = "";
for( var value in arrays )
{
    output1 += arrays[value] + ";";
}

alert( output1 );
//出力
//one;two;three;four;five;
//一応意図した通り


//Array.prottype拡張
//これによりarraysオブジェクトにもlengthAlert 関数が出来上がる
Array.prototype.lengthAlert = function()
{
    //lengthAlert 実装
    alert( this.length );
}

//Array.lengthAlertが呼ばれる
arrays.lengthAlert();    // 5


//もう一度値列挙をする
var output2 = "";
for( var value in arrays )
{
    output2 += arrays[value] + ";";
}
alert(output2);

//出力
//one;two;three;four;five;function () {
//    alert(this.length);
//};

Array.prototypeで拡張したlengthAlertプロパティまで参照される。

おもしろいけど、結構ややこしい(汗)

・for inって順番保証してたっけ?

Javascriptはfor inでのプロパティ列挙の順番を保証していない。との記述。

実際そうだっけ?ということで実験。


var strs = 
{
    one     : "One",
    two     : "Two",
    three   : "Three",
    four    : "Four",
    five    : "Five"
}

strs.six = "Six";
strs.seven = "Seven";

var output = "";
for( var property in strs )
{
    output += property + ";";
}

alert( output );
//one;two;three;four;five;six;seven;

一応順番通りだけど、検証不足か見当違いか。

3.9 グローバル領域の利用を減らす

・グローバル変数を工夫するTips的な

グローバル変数を減らす工夫として次のような方法を使う。


var MyApps = {}

MyApps.AppID = 202110;

MyApps.config = 
{
    IPAddress : "192.168.200.200",
    RootDir : "d:"
}

これならグローバル変数MyAppsだけ考えればよい。

なるほど、ドメイン名などのユニークな値なら衝突の心配はぐっと減る。

仮想名前空間(ネームスペース)的な感じですか。 私はjsファイルを複数読み込んだら名前が衝突してえらいことになった経験あるのでこれは使えます。

ちなみにJavascriptで宣言されたグローバル変数はwindowオブジェクトに格納される。


var hoge = "fuga";

alert (window[hoge] );    //fuga

その他

argments

読書会の中で今のところ記述がなかったけど、 話題としてargumentsオブジェクトがあがった.。 これは私は知らなかったものなので書いておきます。

argumentsオブジェクトは、関数の内部でのみ利用可能で関数に渡された引数値を管理することができる。

最初、可変長引数で使えるのではないかと思いましたが、Javascriptでは引数の数を厳密には見てないのであまり出番はないか。。

逆に引数の数を厳密にするとか?

ひとまずサンプルとしてURLパラメータを付与する関数を作ってみた。


function getAddParamsUrl( url /*, prams */)
{
    var argsCount = arguments.length;

    if ( argsCount < 1 ) return url;

    var val = url + "?";
    for ( i = 1; i < argsCount; i += 2 )
    {
        if ( i == 1 )   val += "&";
        val += arguments[i] + "=";
        val += arguments[i + 1] != null ? arguments[i + 1] : "";
    }

    return val;
}

//実行
    var url = getAddParamsUrl( "../sendmail.php",
                                "courseID", courseID,
                                "chapter", chapter,
                                "section", section,
                                "clause", clause,
                                "page", page );