My practice by Javascript: The Good Parts Section 4.

さて、「第4章 関数」に突入です。ここから javascript の肝なのでしょう。

と、その前に。いよいよ命令文を本格的に記述するようになって気がついたのですが、文末のセミコロンの扱いがとても難しいですね。

基本的に「式文の末尾に必要。ブロックには不要」で合っているとは思うのですが、throw とか do 〜 while とかはパッと見ブロックように見えますし、var でのオブジェクトの宣言も同様なので、とても紛らわしい。

emacs の js2.el が優秀なので助かっていますが、末尾のセミコロンは本当に必要なのでしょうか。ちょっと疑問に思いました。

さて、閑話休題。それでは、関数の章をまとめてみましょう。

関数の呼び出し

ポイントは this の取り扱い。すべてのパターンで同一ではない、というのがミソ

メソッド呼び出しパターン

this: オブジェクトが格納される。直感的。マトモ。

var myObj = {
    value: 0,
    increment: function(inc) {
        document.writeln(this, ' ', this.value);
        this.value += (typeof inc === 'number') ? inc : 1;
    }
};

myObj.increment();
document.writeln(myObj.value);

myObj.increment(2);
document.writeln(myObj.value);
関数呼び出しパターン

this: グローバルオブジェクトが格納される。話がややこしくなる一因。

var a = 1;
var b = 1.0;

var add = function(a, b) {
    document.writeln(this);
    document.writeln(this.a, ', ', this.b);
    document.writeln(arguments, arguments.length);
    for (i = 0; i < arguments.length; i += 1) {
        document.writeln(arguments[i]);
    }

    if (typeof a !== 'number' || typeof b !== 'number') {
        throw {
            name: 'TypeError',
            message: 'add needs numbers.'
        };
    }
    return a + b;
};

var sum = add(3, 4);
document.writeln(sum);

オブジェクトにセットした内部関数と this を共有する場合は工夫が必要。

myObj.double = function() {
    document.writeln(this, ' ', this.value);  // (1) [object Object], 3
    var that = this; // (4) 仕方ないので、this をあらかじめ退避しておく。

    var helper = function() {
        // (3) 同じオブジェクト内なのに this が異なる。
        document.writeln(this, ' ', this.value); // (2) [object DOMWindow] Oh! My God!!
        that.value = add(that.value, that.value);
    };
    helper(); // (5) で、実際に内部関数を使用する。
};

myObj.double();
document.writeln(myObj.value);
コンストラクタ呼び出しパターン

大文字で始まる変数に格納するのが慣例。
this: オブジェクトが格納される。直感的。マトモ。

var Quo = function(string) {
    this.status = string;
    document.writeln(this, ' ', this.status);
};

Quo.prototype.get_status = function() {
    document.writeln(arguments, ' ', arguments.length);
    return this.status;
};

// 命令自体は機能するが、まったく別物。グローバルオブジェクトが格納される。
var myQuoWithoutNew = Quo('confused');
document.writeln(myQuoWithoutNew,
                 ' myQuoWithoutNew is ...',
                 typeof myQuoWithoutNew);

// 推奨できる方法ではないという。上記のような表記ミスを犯す可能性のためか?
var myQuo = new Quo('confused');
document.writeln(myQuo.get_status(),
                 ' myQuo is ...',
                 typeof myQuo);
apply 呼び出しパターン

apply(this, 配列)

var array = [3, 4];
var sumByApply = add.apply(null, array);
document.writeln(sumByApply);

var statusObj = {
    status: 'A-OK'
};

// インスタンスメソッドのような感じ。
// しかし、プロパティ名称の同一性に依存するのは、ちょっと危うい感じがする。
var status = Quo.prototype.get_status.apply(statusObj);
document.writeln(status);

引数

arguments は本物の配列ではない。length しかプロパティを持たない。この件については、先の章で述べられるらしい。

例外

catch ブロックは一つだけ。例外の種類が複数存在する場合は、内部で name による switch や case の分岐か?

var try_it = function() {
    try {
        add('seven');
    } catch(e) {
        document.writeln(e.name + ': ' + e.message);
    }
};

try_it();