月別アーカイブ: 2014年4月

【レビュー】開眼! JavaScript ―言語仕様から学ぶJavaScriptの本質

表紙のフクロウがかわいい開眼! JavaScript ―言語仕様から学ぶJavaScriptの本質を読み終えたのでその感想を書く。

この本は、JavaScriptのprototypeベースのオブジェクト指向について勉強しようと思っているJavaScriptに少し触れた初心者に非常に良い本だと思う。オブジェクト指向について全く知らない人がいきなりサイ本にいくより絶対に良い。

自分はというと愚かなことにサイ本に挑戦してしまった・・・当然いきなりサイ本を読んでも意味がわからない箇所が多く、継承やprototype、closureやapplyなどなど1つずつネットで調べて、自分で書いたコードをリファクタリングしまくって理解するようになった。だから実は既にある程度「開眼!」していたのでこの本で「開眼!」はしなかったのだが、きっとサイ本を手に取る前に読んでいたら「開眼!」していたように思う。

プログラミング経験が超浅い者にとっていきなりオブジェクト指向を理解するのは厳しく、自分も結構時間を割くことになった。また、JavaScriptはプロトタイプベースと言って、Javaのようなメジャーなクラスベースのオブジェクト指向と違うので、ネットでオブジェクト指向について調べても解説しているサイトはクラスベースについてが多かったり、プロトタイプベースを解説しているところでもクラスベースを知ってることを前提に書かれていたりと自分のGoogle検索の仕方が下手なのかもしれないが、ネットで1つ1つ別々に調べていくには少し面倒くさい。(ちなみに自分はこのサイトに非常にお世話になった【javascript初心者から中級者になろう】)

この本はそれらを体系的に学べるのでオブジェクト指向を知らないJavaScript初心者にとっては非常に効率的で有用な1冊である。コードも必要に応じて丁寧に載せられていて1つ1つのコードは非常にシンプルで理解しやすい。

しかし、ある程度オブジェクト指向に理解があったり、JavaScriptの継承の仕方を知っていたりするならネットで充分かもしれない。JavaScriptのプロトタイプベースのオブジェクト指向の基礎的な部分というのはそれ程複雑ではなく、抽象的思考というよりかは覚えるか覚えないかの問題だと思うので、ある程度概要を掴んでいる人ならネットで自分で調べながらコードを書いて記憶に留める作業をした方がいいかもしれない。やっぱり言語について理解するには本を読むだけじゃなくて自分でコード書いてそれを教材にしてリファクタリングしまくるのが自分は良いと思う。

ただ、この本はオブジェクトに関することはとにかく細かく、あまり実用的ではなさそうな部分もしっかり解説されていたり丁寧で面白い。

その他本書について

  • 全体に渡って同じ内容が繰り返し述べられている部分が多い(反復になって覚えやすいと思うか煩わしく感じるかは人それぞれ)
  • ECMAScript3を前提に書かれている
  • アクセサプロパティ(getterとsetter)については載っていない
  • 最後にしっかり理解できているか確認する付録がついており、自分の理解度を計ることができるので便利
  • この本は単行本サイズ、160ページ程で非常に読みやすい
  • 表紙のフクロウがかわいい

ということで丁寧で読みやすい本書は、サイ本に挑戦しようか迷ってるオブジェクト指向を知らない初級者のJavaScripterにオススメ。

自分は次にグッドパーツを読みます~。ヽ(´ー`)ノ

Amazon:開眼! JavaScript ―言語仕様から学ぶJavaScriptの本質

【JavaScript】prototypeのプロパティについて

今日、コードを書いていたら2点重要な事に気付いたので忘れないよう記しておく。

まず1点目、JavaScriptではすべての関数オブジェクトにprototypeプロパティというオブジェクトを持っている。インスタンス生成時、このprototypeのプロパティ(メンバ)が、インスタンスで使えるようになる。そして、このprototypeはインスタンス間で共有される。例えば以下のコード

var Class = function() {};
Class.prototype.prop = 'prototypeのプロパティです';
Class.prototype.obj = {
	prop : 'prototypeのプロパティ(obj)のプロパティです'
};

var a = new Class();
var b = new Class();

a.prop = '代入しますた';
console.log(a.prop);    //代入しますた
console.log(b.prop);    //prototypeのプロパティです

a.obj.prop = '代入しますた';
console.log(a.obj.prop);    //代入しますた
console.log(b.obj.prop);    //代入しますた ←!!

a.obj == b.obj;    //true

この場合、インスタンスであるaもbもpropプロパティとobjプロパティを持っている。(正しくはprototypeのプロパティを参照している。)

この時、

a.prop = '代入しますた';

を実行するとprototypeのpropプロパティに代入されるのではなく、インスタンスaにpropプロパティが新たに作られ、prototypeのpropプロパティはマスキングされる。つまり、prototypeのpropプロパティは変わらないので、

b.prop

の中身は’prototypeのプロパティです’である。

しかし、

a.obj.prop = '代入しますた';

↑のようにprototypeのプロパティがオブジェクトでそのオブジェクトのプロパティの値を変えるような場合、他のインスタンスも影響を受ける。これは、先のpropに代入するような場合のようにインスタンスのプロパティに新たなオブジェクトが作成されたわけではなく、prototypeのプロパティ(obj)にアクセスして、objのプロパティを変えているからだ。prototypeのプロパティは共有されるので他のインスタンスでも変更が反映された。

つまり、prototypeのプロパティがオブジェクトだった場合、個々のインスタンスによってこのオブジェクトに違う値を入れることを想定していると、大変なことになる。

個々のインスタンスで別々のオブジェクトを使いたい場合、コンストラクタ関数(Class)内でオブジェクトを作ることによって解決する。

var Class = function() {
	this.obj = {
		prop : 'プロパティです'
	};
};

//ここで個々のobjプロパティが作成される;
var a = new Class();
var b = new Class();

a.obj == b.obj;    //false

次に2点目。JavaScriptではclosureを使えば変数を外から隠すことができカプセル化を実現できる。と思ったが、prototypeのようなインスタンス間で共有するメソッドをclosureにすると変数もインスタンス間で共有される。以下コード。

var Class = (function() {
	var Class = function() {};
	var x = 1;
	Class.prototype.increment = function() { //closure
		x++
	};
	Class.prototype.getX = function() { //closure
		return x;
	};

	return Class;
}());

var a = new Class();
var b = new Class();

a.getX();    //1
b.getX();    //1
a.increment();
a.getX();    //2
b.getX();    //2

個々のインスタンスで別々の値を持たせたい場合、カプセル化は実現できないように思う。つまり、素直にthis._xのようなプロパティを持つしかない。つまり、解決策はない。どなたかあれば教えてください。カプセル化は実現できないが、プロパティ名の最初にアンダースコアをつけることによって、外からアクセスしないでくださいということを明示するという方法がメジャーなようだ。

1点目は解決策があるので、大したことはなかったけど、2点目に気付いた時はショックだった。varとthisが混ざって、いかにもclosure使ってます!カプセル化実現してます!!closureの利点存分に引き出しています!!!的なコードに仕上がって酔いしれていたので・・・。コンストラクタ関数で定義された変数はprototypeプロパティで使えるというようなことがあればいいなーと思ったりする今日このごろ。

closureの仕様的に無理か・・・