JavaScriptのthis
jsのthisは、ある関数が呼び出されたときに、その関数を格納していたObjectを指す。
関数を単にfunc();という形で単体で実行した場合、thisにはwindowが入る。
new演算子のthis
jsのnewは任意の関数と一緒に呼び出すことができる。
var x = new func();
上の例では、まずnewで新しい空のオブジェクト{}が生成され、次に関数func()が呼び出される。このとき関数func内のthisには新しく生成された空のオブジェクトが渡され、関数実行後、生成されたオブジェクトがnewの実行結果として変数xに代入される。
JavaScriptのプロトタイプベースのオブジェクト指向について、調べたこと
jsは(ES6より前は)クラスを持たないオブジェクト指向言語である。クラスの代わりに、プロトタイプというプロパティを作り、そのプロパティを複数のインスタンスで共有する。そのためプロトタイプベースのオブジェクト指向言語といわれている。
jsの全てのオブジェクト(関数やプロパティの集合)は、__proto__プロパティに「継承したオブジェクト」のメモリアドレスを格納できる。
jsの全てのオブジェクトは、Object.prototypeを継承している。そのため、__defineSetter__, __defineGetter__, toString, hasOwnPropertyなどのメソッドをデフォルトで使うことができる。 下記はコンソールログでオブジェクトを生成し、継承元のObject.prototypeを表示している。
コンストラクタ関数とnew演算子を使ったオブジェクト生成の例
js create object with new Object()
生成したオブジェクトの中身をコンソールで見てみよう!
chromeのコンソールログでまったく同じコードを入力する。
コンストラクタPersonから生成したperson1は、name, a, __proto__の3つのプロパティを持つ。__proto__プロパティには、コンストラクタ関数Personのprototypeのメモリアドレスを格納する。このメモリアドレスを元に、person1はコンストラクタ関数が持つoutputName()を使用することができる。
図で描くとこんな感じ。person1の__proto__にはPerson.prototypeのメモリアドレスが格納されており、Person.prototypeのプロパティの参照が可能である。
一方、person2が持つプロパティはnameと__proto__のみである。person2.aが定義されていない場合は、継承元である上位のPerson.prototypeを探索しに行く。もし見つからなければ最上位の Object.prototype までプロパティを探索しにいく。
オブジェクトの継承過程をたどる
Personコンストラクタから生成したオブジェクトperson1は、コンストラクタ関数とは別のメモリに格納される。オブジェクトによって、プロトタイプチェーンが異なる。
オブジェクトperson1は、下記のようになる。
person1 < Person < Object < null (上位の継承オブジェクトがない)
コンストラクタPersonは、下記のようになる。
Person < Empty < Object < null (上位の継承オブジェクトがない)
コンソールで見てみよう。
function Empty()のプロトタイプにはapplyやcallメソッドが定義されている。
Javascriptでclosureと、applyメソッド、callメソッドを使う
jsが持つ関数スコープを利用してクロージャー(関数閉包)を書くことができる。
上記の例では、関数countの中のローカル変数iを参照できるのは、無名関数が入っている変数xを実行したときのみである。変数iをグローバル変数にすると他の関数からの参照が可能になり、意図せずに値を変更してしまう可能性がある。
jsの全ての関数は組み込み関数Functionを継承しており、組み込み関数Functionが持つapplyメソッド、callメソッドも継承している。
callメソッド:現在のオブジェクトが持っていないメソッドを適用する。
applyメソッド:callメソッドと同じように現在のオブジェクトの代わりに、他のオブジェクトのメソッドを適用できる。引数に可変長の配列をひとつとる点がcallと異なる。以下のような感じ。
Javascriptのスコープ(関数、グローバル、ES6はブロック)と巻き上げについて
jsのローカルスコープは関数内のみを参照できることであり、グローバルスコープはscriptのどこからでも参照できることを指す。ローカルスコープを持つ変数をローカル変数、グローバルスコープを持つ変数をグローバル変数という。
スコープは変数が定義された位置で決まり、関数の外で定義されればグローバル変数、関数の中で定義されればローカル変数となる。
- 関数の外でグローバル変数を定義し、中でローカル変数を定義できる。関数内でもvarをつけなければグローバル変数を定義できる。
- 変数の巻き上げ(hoisting)で、グローバル変数への参照を、ローカル変数への参照へスイッチできる。
jsでは、関数内のどこからでもローカル変数を定義できるが、関数内のどこで定義されても、その関数内の先頭で定義されたことになる。
上記のような結果になるのは、関数の先頭で、var global;という中身のない変数が定義され、globalという名前空間がローカルスコープを参照するようになったためである。=>変数定義は必ず先頭で行うこと、でないとローカル変数を参照しているかグローバル変数を参照しているかわからなくなります。
- ブロックスコープはES6からサポートしている。
ECMAScript6より前の仕様では、ブロックスコープを採用していなかった。
ES6以降はletを使うと、ブロックスコープを作れるでござる。
ES 6で開発するにはBabelか、
気軽に試したかったら、fiddleで試してみてください。
Javascriptが持つ関数型言語の特徴(第一級関数、高階関数)
jsはプロトタイプベースのオブジェクト指向言語と呼ばれているが、関数型言語の性質も持ち合わせている。
第一級オブジェクト(first-class object)と第一級関数(first-class function)
第一級オブジェクトとは、プログラムにおいて、生成、代入、演算、(引数、戻り値)としての受け渡し、など基本的な操作を制限なしに使用できるオブジェクトを指す。第一級関数は、関数を第一級オブジェクトとして扱うことのできるプログラミング言語の性質を指す。言語によって、第一級オブジェクトと第一級関数の性質は異なってくるが、jsでは以下の点をサポートしている。
- 関数をリテラルとして扱うことができる。無名関数を生成し、変数に代入できる。(リテラルとは、数値や文字列など、プログラムで特定のデータ型を用いて直接表記された値のこと。書式を指すこともある。)
-
関数を動的に生成できる。 js first-class object and first-class function
高階関数(higher-order function)
高階関数とは、関数を引数に取る関数のこと。
- 引数に関数を渡すことができる。
- 戻り値に関数を渡すことができる。
MVCアーキテクチャについて調べたこと
MVCアーキテクチャは、ソフトウェアの構成と役割について言及した概念のひとつ。1978年にXerox Parc(パロアルト研究所)のTrygve Reenskaugが、Smalltalk-76で使われているMVCモデルについて紹介したのが始まりと言われている。Trygve Reenskaug本人のウェブサイトには以下の記述がある。
The essential purpose of MVC is to bridge the gap between the human user's mental model and the digital model that exists in the computer.
MVCの目的は、開発者が作りたいソフトウェアのイメージを、コンピューターのデジタルモデルに変換すること。
1988年に、MVCについて書かれた論文が公開された。
"A Cookbook for Using the Model-View-Controller User Interface Paradigm in Smalltalk-80"
この論文で、MVCアーキテクチャの各コンポーネントの役割と依存関係がまとめられている。18ページとそんなに長くないのでいつか見てみよう。
ユーザーインターフェースを持つアプリケーションを設計するアーキテクチャ。
アプリケーションの内部処理と、ユーザーに対する入出力処理を分離することが目的。
アプリケーションを3つのレイヤーに分割し、それぞれが決められた役割を担当する。
Model (data, logic, rule)
アプリケーションデータ、ビジネスルール、ロジックなどの処理を持つ。
View (UI, presentation, logic)
テキスト、グラフ、図など、情報表現をする。
Controller (façade, flow controll)
ユーザーからの入力を受け取り、ModelとViewへの命令に変換する処理を持つ。
(※façadeとは、Gof(Gang of Four)によって定義された、コンピューターソフトウェアのデザインパターンのひとつ。直訳すると「建物の正面」。処理時間の独立性を高めるために、処理を呼び出す単純な操作だけを持ったクラスを提供すること。)
Server-side MVC と Client-side MVC の違い
Server-side MVCモデルを採用したアプリケーションは、基本的にHTTPリクエスト-HTTPレスポンスのコミュニケーションをサーバーと行う。Viewの生成はサーバーサイドで行う。
Client-side MVCモデルを採用したアプリケーションは、Ajax(非同期通信)またはHTML5のWebSocketを用いて、サーバーとの通信を行う。Viewの生成はクライアントサイドが動的に行う。ブラウザのローカルストレージにデータを保存し、サーバーとの通信時間を少なくする。
WebSocket: webサーバーとwebブラウザ間の双方向通信用のプロトコル。TCP上で動く。サーバーとブラウザがハンドシェイクを行い、持続的なコネクションを確立した後は、必要な通信を全てそのコネクション上で、専用の軽量プロトコルを用いて行う。サーバー、ブラウザのどちらからでもデータの送信を開始できる。
URIスキーマはws:。wss:はセキュアなWebSocket通信を実現する。
W3CはAPIの規格を策定中。The WebSocket API
IETFはプロトコルの規格を策定中。RFC 6455 - The WebSocket Protocol
localstorage: ブラウザ側でKey-Value型のデータを保存する機能。クッキーの保存容量が4KBytesなのに対し、ローカルストレージでは5MBytesまでのデータを無期限で保存可能。
MVCモデルから派生したMVPモデル、MVVMモデル、MVWモデル
新技術の普及を背景に、MVCモデルでは整理しきれなくなったデータやロジックを誰が担当するか、という問いの解決策として新たなデザインパターンが派生した。
MVPモデル:モデル、ビュー、プレゼンターからなる。MVCモデルではどこにも属さなかった、ビューに関する動的なデータ(他の値によって、値を変える背景色などのデータ)とそのロジックはプレゼンターが担当する。モデル自身はビューやプレゼンターに依存せず、純粋なビジネスロジックのみを持つ。ビューは画面の表示とユーザー入力の受付口を担当し、入力されたデータをプレゼンターに渡す。
MVVMモデル:ビジネスロジックとプレゼンテーションロジックを分けることを目的としたデザインパターン。ViewはUIの外観と構造を定義し、いくつかのプレゼンテーションロジックを含む。ViewModelはプレゼンテーションロジックとステート(=状態)を含む。Modelはビジネスロジックを担当する。ViewのDataContextプロパティにViewModelのインスタンスを設定することで、ViewはViewModelとデータバインドを行う。
MVWモデル:GoogleのIgor MinarがAngular.jsを紹介するときに用いたデザインパターン。Model-View-Whateverの略。プレゼンテーションの中で"Whatever stands for 'whatever works for you'."「Whateverの意味は動けばなんでもいいということ」と紹介している。MV*とも書く。
オブジェクト指向と、言語の進化を辿るということ
オブジェクト指向とは、属性と振る舞いを持つオブジェクトを作るための「型」を作ることを意識しながらプログラムを書くこと。
と言われても理解は深まらないので、オブジェクト指向ではない指向とはどんなものかを理解して、比較をすることで、理解を深めよう。比較対象はプロセス指向である。
処理にデータを渡し、値を得ることをプロセス指向と呼ぶ。プロセス指向とは、プログラムはデータと処理からできており、プログラム内でデータは変数、処理は関数として表される。データは処理の対象であり、処理にデータを渡して値を求める。
オブジェクト指向とは、プログラムはオブジェクトとメッセージからできており、プログラム内でオブジェクトは「属性(パラメータ)」と「振る舞い(クラスメソッドやインスタンスメソッド)」を持ち、メッセージは振る舞いを呼び出す「処理」として表される。プログラムは複数のオブジェクトで構成され、オブジェクトは処理能力を持つデータである。オブジェクト(データ)にメッセージ(処理)を渡すことで値を求める。
プロセス指向であるC言語と、オブジェクト指向プログラミングができるJavaの文法を比較する
【C言語で文字列の長さを求める】
strlen(s)
処理strlen()にデータsを渡している。
【Javaで文字列の長さを求める】
s.strlen()
自身の長さを求める処理を持つデータsに、処理を呼び出すメッセージstrlen()を渡している。
※JavaのクラスライブラリにはMath.sqrt(3)のように属性のないクラスメソッドが存在する。Javaは純粋なオブジェクト指向ではなく、開発者の志向によって、プロセス指向で書かれたり、オブジェクト指向で書かれたりする。
オブジェクト指向プログラミング(OOP: Object Oriented Programming)について
現在普及しているOOP言語にはC++/Java/C# がある。次にOOPで使われる言葉の定義をいくつか記す。この定義は上記の3言語で共通の意味を持つ。
クラス:データと処理をまとめたモジュール、複数の型のパラメータを持つ複合データ型。
クラスのインスタンス:クラスをデータ型とした変数、メモリにロードされたもの。
オブジェクト:クラスインスタンスの別名。
クラスがあることによって使える機能に、カプセル化・多態性がある。
カプセル化(encapsulation):システムを独立した小さなプログラムの集合体として管理できる。モジュール間のつながりは関数呼び出しのみであり、変更に強い。不必要な機能は非公開にし、プログラムをシンプルに保つ。
多態性(polymorphism):クラス型が異なっても、同じ機能には同じ名前がつけられるので、人間の感覚では同じ関数が対象となるオブジェクトの型によって異なる手続きを行っているように見える。
OOPに役立つ、23種類の設計パターンのアイデア集のこと。生成・振る舞い・構造のパターンに分類できる。
言語の進化を辿るということ
ちょうど1年前にC言語の勉強を始めた頃、それまでRubyしか触れたことのなかった私は、ポリモーフィズムが素晴らしいアイデアなのだと知った。最近はSmalltalkが純粋なOOP言語であることを知り、言語の進化の歴史を知ることができた。Smalltalkは発明当時では画期的なアイデアであるOOPを採用したが、制御構造もデータ型も演算子もなく、すべてをメッセージとして実行するという欠点があった。
例えば、Smalltalkで下記を実行すると16を返す。
3+5*2
メッセージに優先順位がなく、左から順に実行されるからだ。これでは数学の演算ルールと異なるので、JavaやC言語に慣れた多くの人は使いにくいと感じるだろう。現在では、プログラミング言語は数学の演算法則に従うことが当たり前だからだ。
Rubyが影響を受けた言語を辿ると言語の進化を見ることができる。Rubyは「オブジェクト指向の動的型付け言語」と言われている。Rubyは強い動的型付けを行う。動的型付け言語とはプログラムの実行時に変数の型を決める柔軟な言語のことである。例えば、関数の実引数と仮引数の定義時にデータ型を定義せずに済み、シンプルなプログラムの記述が可能となる。Rubyに影響を与えたと言われる言語を遡ると、「プロセス指向静的型付け言語」から徐々に現在の形に進化を遂げていることがわかる。例えば、C言語は弱い静的型付け、C言語に影響を与えたALGOLは強い静的型付けだった。
言語の進化により、開発者はストレスなくプログラミングを楽しむことができるようになる。また、言語の歴史を知ることで、言語に親しみと感謝を持つようになるので、技術の歴史を学ぶことはやはり重要なのだと思う。