読者です 読者をやめる 読者になる 読者になる

『Effective JavaScript』を読んで(項目18~22)

『Effective JavaScript』を読んでシリーズ。

項目18:関数、メソッドコンストラクタの、呼び出しの違いを理解する

通常のプログラミング言語では、関数、メソッドコンストラクタを別々の存在として考えるだろう。一方、JavaScript ではたった一つの「関数(Function)」という構造を、それぞれのパターンに応じて利用する。

// 1. 関数(ファンクションコール)としての利用
function hello(username) {
    return "hello, " + username;
}
hello("hjm333") // => "hello, hjm333"

// 2. メソッドとしての利用
var obj = {
    hello: function() {
        return "hello, " + this.username;
    },
    username: "hjm333"
};
obj.hello(); // => "hello, hjm333"

// 3. コンストラクタとしての利用
function User(name, passwordHash) {
    this.name = name;
    this.passwordHash = passwordHash;
}
var u = new User("hjm333", "hash");
u.name // =>  "hjm333"

2 のケースでは、this は hello のレシーバーである obj に結合される。obj 内で定義されたから this が obj に結合されているわけではない点に注意する。

3 のケースでは、できたてのオブジェクトが this に結合される。new 演算子付きで関数を呼び出すことがきっかけとなる。new 演算子つきで呼び出さないと通常の関数呼び出しとして処理されて、意図通りに動作しない。

通常の関数定義内でも this は参照できる。レシーバを指定しない呼び出しでは this にグローバルオブジェクトが束縛される。この挙動は問題を引き起こすことが多いため、ES5 の strict モードでは this はデフォルトで undefined に束縛される。

var username = "hjm333"

function helloWithNoStrict() {
     return "hello, " + this.username;
}

function helloWithStrict() {
    "use strict";
    return "hello, " + this.username;
}

helloWithNoStrict(); // => "hello, hjm333"
helloWithStrict(); // => Error: undefined に "username" プロパティが存在しない

項目19:高階関数を快適に使えるようになろう

高階関数使おうね!という話。特にメモするような内容なかった。

項目20:カスタムレシーバ付きでメソッドを呼びだすには call を使う

通常、関数内のキーワード this に束縛される値は、呼び出し側の構文によって決まる。レシーバー付で呼び出せばそのレシーバーが束縛されるし、レシーバがなければグローバルオブジェクトが束縛される。

関数に任意のレシーバーを与えたい場合には call メソッドが利用できる。

function hello(arg) {
    return arg + ", " + this.name;
}
var obj = {
    name: "hjm333"
};
hello.call(obj, "hello");

こうすると、obj に hello を追加することなく、this に obj をバインドすることが出来る。

この後、Dictionary を使った説明が続くのだが、いまいち理解できなかったので省略。 コールバックを利用する関数を作るときには call が役に立つということだけ分かったので書いておく。

項目21:いくつでも引数をとれる関数を呼び出すには apply を使おう

任意の引数をとる関数を可変長引数関数(variadic function)、または可変アリティ関数(variable-arity function)と呼ぶ。関数のアリティ(arity)とは関数が期待する引数の数のことである。固定長の引数を期待することを固定アリティ(fixed-arity)と呼ぶ。

apply メソッドは可変長引数関数のために用意されたメソッドで、引数の配列を受け取りその配列の個々の要素を、個々の引数として特定の関数に与えるためのメソッドである。

// average は可変長引数関数
average(1, 2, 3);
average(1);
average(1, 2, 3, 4, 5);

var scores = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100];
average.apply(null, scores); // 第一引数はカスタムレシーバを与える

可変長引数関数の作成方法は事項で説明する。

項目22:可変長引数関数を作るには、argumentts を使う

JavaScript で可変長引数関数を定義するには暗黙的なローカル変数 arguments を利用する。

function average() {
    for (var i = 0, sum = 0, n = arguments.length; i < n; i++) { 
        sum += arguments[i];
    }
    return sum / n;
}
average(1, 2, 3);
average(1);
average(1, 2, 3, 4, 5);

経験則として、可変アリティ関数を提供するときは、明示的に配列をうけとる固定アリティバージョンも提供するべき(らしい)。可変アリティ関数は、固定アリティ関数に作業をゆだねる薄いラッパーであるのが典型。

function average() { 
    return averageOfArray(arguments);
}

function averageOfArray(a) {
    for (var i = 0, sum = 0, n = arguments.length; i < n; i++) { 
        sum += a[i];
    }
    return sum / n;
}