おもしろwebサービス開発日記

Ruby や Rails を中心に、web技術について書いています

javascriptの基礎その2(関数について)

今日は関数について。サイ本8章を読んで気になったところのメモです。

関数の引数

引数は省略できるが、省略した分の引数には未定義値が設定される。定義したよりも多くの引数を渡したときは、多い分は無視される。(でも後述するArgumetsオブジェクトを使えば参照できる)

Argumentsオブジェクト

Argumentsオブジェクトは配列のようなオブジェクトで、関数に渡された引数がarguments[]配列の要素に格納される。Argumentsオブジェクトを使用することで関数で定義したよりも多くの引数を渡すことができる。また、引数の個数のチェックをするときなど便利。Argumentsオブジェクトの中身を変更すると、引数も変更されるので注意。

Argumentsオブジェクトを利用して引数の個数をチェック

こんな感じで引数個数のチェックができる。

function f(x, y, z) {
  if(arguments.length != 3) {
    throw new Error("function f called with" + arguments.length + "arguments, but it expects 3 arguments.");
  }
  // 実際の処理を書く
}
Argumentsオブジェクトを利用して任意の個数の引数を受け取る関数を定義

こんな感じで、任意の個数の引数を受け取る関数を定義できる。ちなみにこのような関数を可変長引数関数やvarargs関数と呼ぶらしい。 

function max (){
  var m = Number.NEGATIVE_INFINITY;
  for(var i = 0; i < arguments.length; i++) {
    if ( arguments[i] > m) m = arguments[i];
  }
  return m;
}

var largest = max(10,100,2,3);
calleeプロパティ

Argumentsオブジェクトにはcalleeプロパティが定義されている。calleeプロパティは、現在実行中の関数を参照する。calleeプロパティを利用すると匿名関数で再帰ができる。

function(x) {
  if(x <= 1) return 1;
  return x * arguments.callee(x-1);
}

一瞬、これってちょっと前に話題になったカリー化と関係してんのかなーと思ったけど調べたら違った。

カリー化(currying)とは、計算機科学分野の技法の一つ。複数の引数をとる関数を、引数が「もとの関数の最初の引数」で戻り値が「もとの関数の残りの引数を取り結果を返す関数」であるような関数にすること。

カリー化 - Wikipedia

カリー化はまた今度調べるつもり。

関数リテラル

関数リテラルと無名関数は同じだと思ってたけど、関数リテラルには関数名を指定してもいい*1みたい。再帰の関数を定義するときに使う。下のコードはぱっと見普通の関数定義文を変数に代入しているように見えるけど、これも関数リテラル

var f = function fact(x) { if (x <= 1) return 1; else return x*fact(x-1);};

でもこれよりもcalleeプロパティ使った方がスマートな気がするな。この書き方はあんまり使わないかも。

データとしての関数

関数名を変数に渡すと、関数の参照を変数に渡したことになる。

function square(x) { return x*x }
var a = square; // aはsquareと同じ関数を参照する

関数のプロパティ

関数もオブジェクト。プロパティが設定できる。

lengthプロパティ

lengthプロパティには、宣言時の引数の個数が入っている。

独自のプロパティの定義

こんな感じでグローバル変数っぽく使える。名前の衝突がない分、関数に定義した方が便利。

// 関数宣言はコードの実行より前に処理される!ので関数宣言の前に代入可能
uniqueInteger.counter = 0;
function uniqueInteger() {
  return uniqueInteger.counter++;
}

apply(), call()

全ての関数でcall()メソッドとapplyメソッドが使える。
あるオブジェクトのメソッドであるかのように関数を呼び出すことができる。
関数f(x, y)を、オブジェクトoのメソッドであるかのように呼び出すにはこうする。

f.call(o, 1, 2);
f.apply(o, [1,2]);

callとapplyの違いは引数の渡し方だけ。

匿名関数を定義してすぐ実行するコード

こんな感じで名前空間を汚さないように関数を実行できる。

(function (){
  // 処理
})();

スコープチェーン、クロージャ

覚えるのがめんどいのであとまわし。

参考

JavaScript 第5版

JavaScript 第5版

*1:この書き方はjavascipt1.5以降のサポート。