Object.create(...)でオブジェクトを作る(シリーズその5)

More than 3 years have passed since last update.

「必要なのはJavaScript言語仕様書とブラウザーコンソールだけ!」

シリーズ、その5。


Object.create で最も単純なオブジェクトを作る

Object.create 関数の仕様によると、

引数として渡されたプロトタイプを持つオブジェクトを生成するとあり、Nullも渡せると書いてある。

できるだけ単純なオブジェクトから作ってみることにする。


Nullを渡してオブジェクトを作ってみる。

var o1 = Object.create(null)

o1

2015-12-09 19_07_13-Cortana.png

問題なく作れた。これはいったいどういうオブジェクトなんだろう。


オブジェクトのプロトタイプを調べる

では、Object.getPrototypeOf() 関数を使って、o1のプロトタイプを調べてみる。

予想はNullが返される。

Object.getPrototypeOf(o1)

2015-12-09 19_09_59-Start.png

結果は予想どおり。

さて、このプロトタイプが無い(Nullな)オブジェクトだけど、一体役に立つのだろうか?


プロトタイプを持たないオブジェクトの性質

prototype の利用目的は継承とプロパティの共有なので、逆に言うとprototypeを持たないオブジェクトはそのどちらも利用出来ないということになる。

コンソールに

var o1 = Object.create(null)

o1.toString()

っと打ち込むと

2015-12-09 19_26_33-Start.png

このように怒られてしまう。toString()なんてありませんよ、と言っている。

なぜこうなるかというと、o1は「プロパティの共有」で toString をどこかから探してくる、ということが出来ないからである。

o1はこの状態だと誰からも何も継承しないし共有もしないので、全くもって役に立たない、天涯孤独な寂しいオブジェクトを作ってしまったわけだ・・・


プロトタイプを持たないオブジェクトにせめて自分だけのプロパティを!

誰ともプロパティを共有していないからといって、自分専用のプロパティを持ってはいけない、ということにはならない。

Object.defineProperty() 関数を使って新しくプロパティを加えてあげよう。

var o1 = Object.create(null)

Object.defineProperty(o1, "id", {value : 123});
o1.id

2015-12-09 19_36_21-Start.png

idという独自のプロパティを持たせることが出来た。

こうやってちまちまとオブジェクトをほぼゼロから定義していくやり方もある。可能だしJavaScriptの理解の助けにはなるが、実際的には役に立ちそうにない。


Null以外で単純なオブジェクトを作る

プロトタイプがNullだとつまらないので、せめてObjectオブジェクトのプロトタイプを指定して新規オブジェクトを作ってみる。

var o2 = Object.create(Object.prototype)

o2.toString()

2015-12-09 19_47_26-Start.png

今度はtoStringという名前のプロパティが見つかった。呼び出しが成功し、オブジェクトをStringで表す値が返されている。さて、このtoString関数は一体どこから継承してきたのかというと、それはo2のプロトタイプである Object.prototype からである。

ちょっとややこしくなって来たけど、コンソールで確認してみる。

Object.getPrototypeOf(o2) == Object.prototype

結果:

2015-12-09 19_49_48-Start.png

o2のプロトタイプはObject.prototypeと同じだと出た。

当然といえば当然。何しろ

var o2 = Object.create(Object.prototype)

と指定したわけだから。

要点

Object.createにprototypeを渡すと、それをプロトタイプとするオブジェクトを生成してくれる。


誰がそのプロパティを持っているか?

継承とか共有が起こると、そもそも誰のモノなのか、といったあたりで混乱が起こりやすい。とくにバグで予想外の結果が出る場合は、想定されていないオブジェクトのプロパティが使われていたりする可能性がある。

Object.getOwnPropertyNames(O) を使って、オブジェクトが独自に定義しているプロパティをリストアップすることが出来る(リストが空の場合もありうる)

以下、コンソールでo2と、そのプロトタイプであるObject.prototypeそれぞれのプロパティをそれぞれリストアップさせてみる。

Object.getOwnPropertyNames(o2)

Object.getOwnPropertyNames(Object.prototype)

結果:

2015-12-09 19_56_08-Start.png

o2は何も独自に定義していないので、返された値は要素ゼロの列。

対して、Object.prototype の方はいくつものプロパティを定義しているのが分かる。

そして toStringはObject.prototypeしか定義していなから、o2.toString()はそれを継承することになる。

要点

オブジェクトは、そのオブジェクトのprototypeのプロパティを継承する。

ということはJavaScriptプログラミングではprototypeのプロパティにどんなものがあるのかを知ることが重要になってくる。Object.prototypeも含めて built-inオブジェクトのprototypeをより理解することが大切だということだ。


built-inオブジェクトのprototypeを調べる

ここではいくつかの built-in オブジェクトのprototypeをObject.getOwnPropertyNamesを使って見てみる。

JavaScript関連の本やリファレンスを見ても分かることだが、コンソールと対話式で調べた方が便利な場合も多いかもしれない。

調べるプロトタイプは:


  • Object.prototype

  • Function.prototype

  • String.prototype

  • Array.prototype

Object.prototype

> Object.getOwnPropertyNames(Object.prototype)

< ["constructor", "toString", "toLocaleString", "valueOf", "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable", "__defineGetter__", "__lookupGetter__", "__defineSetter__", "__lookupSetter__", "__proto__"]

最もよく使うtoStringメソッドはここで定義されている。

hasOwnPropertyisPrototypeOfなどは動的に型を扱えるJavaScriptの特徴的なメソッドだと言える。

Function.prototype

Object.getOwnPropertyNames(Function.prototype)

["length", "name", "arguments", "caller", "constructor", "bind", "toString", "call", "apply"]

もっとも関数らしいメソッドがcall。他にもnamearguments、そしてcallerなんてものもある。

この中で特に興味深いのはbindメソッド。これは後述するが、オブジェクトのインスタンスとFunctionオブジェクトと深い係わりがある。

String.prototype

Object.getOwnPropertyNames(String.prototype)

["length", "constructor", "valueOf", "toString", "charAt", "charCodeAt", "codePointAt", "concat", "endsWith", "includes", "indexOf", "lastIndexOf", "localeCompare", "match", "normalize", "repeat", "replace", "search", "slice", "split", "substring", "substr", "startsWith", "toLowerCase", "toLocaleLowerCase", "toUpperCase", "toLocaleUpperCase", "trim", "trimLeft", "trimRight", "link", "anchor", "fontcolor", "fontsize", "big", "blink", "bold", "fixed", "italics", "small", "strike", "sub", "sup"]

文字数を返すlengthはよくあるプロパティ。

startsWithendsWithincludesなどのサーチ系もあるが、matchという、正規表現を使用する強力なものもある。

toLowerCasetoUpperCaseなどのトランスフォーム系、それにconcatsplitなどのC++やC#でお馴染みの便利メソッドがあるのが分かる。

Array.prototype

Object.getOwnPropertyNames(Array.prototype)

["length", "constructor", "toString", "toLocaleString", "join", "pop", "push", "reverse", "shift", "unshift", "slice", "splice", "sort", "filter", "forEach", "some", "every", "map", "indexOf", "lastIndexOf", "reduce", "reduceRight", "entries", "keys", "copyWithin", "find", "findIndex", "fill", "concat", "includes"]

C++やC#に慣れている人にとってはpoppushそれにconcatなどは軽い驚きを与える。それらの言語では Array は要素数が固定されているの、それをスタックのように使うには別のデータストラクチャーを使わなくてはならない。JavaScriptのArrayはより柔軟だということが分かる。

findfindIndexなどのサーチ系は一般的。

forEachを使ってそれぞれの要素をインデックスなどを使わずに処理することもできる。

要点

built-in オブジェクトのprototypeのプロパティを知ることがJavaScriptプログラミングの基礎になる。

(つづく)