紳士なブログ

紳士すぎてすみません

javascriptの勉強 その3 -関数-

引き続き、javascriptの勉強をしていきます。

今回は少し重めの関数の話。
それでは早速。


  • 関数は一連の命令文を内包したjavascriptの基本的な部品である
    • 関数は、コードの再利用、情報の隠蔽、プログラムの構造化のために利用される
    • また、関数はオブジェクトの振る舞いを規定するためにも利用される
  • 一般的にプログラミングを行うということは、一連の要求仕様を関数とデータ構造に分解するための作業である


  • Functionオブジェクト
    • javascriptにおける関数はオブジェクトである
      • オブジェクトは複数の名前と値のペアで構成されており、プロトタイプオブジェクトへの隠れたリンクをもっている
    • FunctionオブジェクトはFunction.prototypeにリンクされている(そしてそれはObject.prototypeへリンクされている)
    • すべての関数は、生成される際にさらにもう2つの隠されたプロパティを持つようになっている
    • それは、その関数の振る舞いを決めるためのコンテキストとコードである
    • 関数オブジェクトもすべて、生成時にprototypeプロパティが同時に生成される
      • その値はオブジェクトであり、そのconstructorプロパティにはその関数自身がセットされている
    • 関数はオブジェクトなので、それ以外の値と同様に扱うことができる
      • 関数を変数やオブジェクト、配列に格納することもできる
      • 関数が他のオブジェクトと異なるのは、それを呼び出すことができるという点である



// addという変数を定義し、2つの値を加算する関数を格納する
var add = function (a, b) {
  return a + b;
}


  • 関数リテラルつづき
    • 関数リテラルは4つのパーツで構成される
      • 1番目はfunctionという予約語
      • 2番目は関数の名前で、これは省略可能
        • 関数に名前をつけることで、再帰的に呼び出すことが可能となる
      • 3番目は括弧で囲まれた関数のパラメータである
        • 通常の変数がundefinedで初期化されるのと異なり、これらの変数はその関数が呼び出された際に、渡された引数で初期化される
      • 4番目は中括弧で囲まれた、命令文の集合体である
        • これらの命令文は関数の本体であり、その関数が呼び出された際に実行される
    • 関数リテラルは、式を記述可能な場所であればどこにでも記述することができる
      • 関数を他の関数の内部で定義することもできる
      • 内包された関数は、もちろんそれ自身のパラメータや変数にアクセスできるが、それだけでなく、その関数を内包している外側の関数のパラメータや変数にアクセスすることもできる
      • 関数リテラルで定義された関数オブジェクトは、外部コンテキストへのリンクも保持していると言える
      • この機能は「クロージャ」と呼ばれ、これを活用することで、かなり高い表現力を得ることができる


  • 関数の呼び出し
    • 関数の呼び出しを行うと、現在実行されている関数の処理は一旦停止され、コントロールとパラメータが新しい関数へと渡される
    • 定義されたパラメータに加えて、すべての関数はあと2つのパラメータを自動的に受け取っている
      • それはthisとargumentsである
      • thisパラメータはオブジェクト指向プログラミングにおいては非常に重要なもので、その値は呼び出しのパターンによって異なる
      • javascriptの呼び出しのパターンは、メソッド呼び出しパターン、関数呼び出しパターン、コンストラクタ呼び出しパターン、apply呼び出しパターンの4つの種類があるが、これらの違いは、thisという追加パラメータがどのように初期化されるか、という点にある


  • メソッド呼び出しパターン
    • 関数がオブジェクトのプロパティとして格納されている場合には、メソッドと呼ばれる
    • メソッドが呼び出された場合、thisにはそのオブジェクトが格納される


// myObjectを生成する
// これはvalueプロパティとincrementメソッドをもつ
// incrementメソッドはパラメータを1つとり、これは省略可能である
// 引数が数値ではなかった場合、
// 代わりにデフォルト値として1が使われる

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

myObject.increment();
document.writeIn(myObject.value); // 1

myObject.increment(2);
document.writeIn(myObject.value); // 3


  • メソッド呼び出しパターンのつづき
    • メソッドではthisを使ってオブジェクトにアクセスできるので、オブジェクトから値を取得したり、オブジェクトの内容を修正したりすることができる
    • thisへのオブジェクトのセットは、呼び出しが発生したタイミングで行われる
    • 遅延束縛と呼ばれるこの割り当てのタイミングのおかげで、thisは非常に便利なものとなっている
    • thisを使って自分自身のオブジェクトコンテキストにアクセスしているメソッドは、パブリックメソッドと呼ばれる


  • 関数呼び出しパターン
    • 関数がオブジェクトのプロパティではない場合は、関数として呼び出される
    • このパターンで呼び出された関数では、thisにはグローバルオブジェクトがセットされる。これは言語の設計としては失敗である。
      • これには回避策が存在する。
      • もしメソッド内で別の変数を定義し、その中にthisの値を代入すれば、内部関数ではその変数を通じてthisの値にアクセスできるようになる


var sum = add(3, 4); // sumは7


// myObjectにdoubleメソッドを追加する

myObject.double = function() {
  var that = this; // 値の待避

  var helper = function() {
    that.value = add(that.value, that.value);
  };

  helper(); // helperを関数として呼び出す
};

// doubleをメソッドとして呼び出す

myObject.double();
document.writeIn(myObject.value);


  • コンストラクタ呼び出しパターン
    • javascriptは、プロトタイプ継承を行う言語である
      • これは、オブジェクトが他のオブジェクトから直接継承を行うことを意味する
      • そしてこの言語にはクラスの概念は存在しない
    • もし関数を呼び出す際に、new演算子が前に付けられていた場合、新しいオブジェクトが生成されて、thisにはその新しいオブジェクトがセットされるようになる
    • そしてそのオブジェクトは、呼び出された関数のprototypeプロパティへの隠されたリンクをもっている


// Quoという名のコンストラクタ関数を生成する
// これはstatusプロパティをもつオブジェクトを生成する

var Quo = function (string) {
  this.status = string;
};

// get_statusというパブリックメソッドを
// Quoのすべてのインスタンスで利用可能にする

Quo.prototype.get_status = function () {
  return this.status;
};

// Quoのインスタンスを生成する

var myQuo = new Quo("confused");

document.writeIn(myQuo.get_status()); // confused


  • コンストラクタ呼び出しパターンつづき
    • new演算子をつけて呼び出すことを前提とした関数は、コンストラクタと呼ばれる
      • コンストラクタは、大文字で始まる名前の変数に格納されるのが慣例である


  • apply呼び出しパターン
    • javascriptは関数型オブジェクト指向言語であり、関数はメソッドをもつことがきる
    • applyメソッドを使うことで、引数を格納した配列を使って関数を呼び出すことができる
    • さらに、applyメソッドを使うことでthisにセットされている値を自由に設定することが可能になる
      • applyメソッドには2つのパラメータを指定することができ、1つ目はthisにセットしたい値、2つ目はパラメータの配列である


// 2つの数値からなる配列を作り、それらを足し合わせる

var array = [3, 4]
var sum = add.apply(null, array); // sumは7

// statusというメンバをもつオブジェクトを生成する

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

// statusObjectはQuo.prototypeを継承していない
// しかし、statusObjectがget_statusメソッドを持っていないにも
// かかわらず、statusObjectのget_statusメソッドを呼び出すことが可能になる

var status = Quo.prototype.get_status.apply(statusObject);

// statusは'A-OK'


  • 変数型の拡張
    • javascriptでは、標準で用意されている変数型を拡張することができる
    • 以前、Object.prototypeを使うと、すべてのオブジェクトで利用できるメソッドを追加できることを紹介したが、関数や、配列、文字列、数値、正規表現、真偽値においても、同様のことが言える
    • 標準で用意されている変数型を拡張することで、言語の表現力を飛躍的に高めることができる
      • javascriptのプロトタイプ型のインターフェイスがもつ動的な性質のおかげで、メソッドを追加すると、その変数型の値すべてが即座にそのメソッドを利用できるようになる
      • たとえば、Function.prototypeを拡張することで、すべての関数で利用できるメソッドを追加することができる


Function.prototype.method = function (name, func){
  this.prototype[name] = func;
  return this;
};


ひとまず今回はここまでにします。
長々とお付き合いいただきありがとうございます。