Javascript普通に書いてたら、DOMいじることが多く、ガッツリとオブジェクトを書くことが少ない。ちょっと再帰処理をやろうとおもったら、嵌まり込みそうになった。
最近のJavascriptにおける継承と制御についてまとめておいた。
昔からあるJavascriptのnew
var Cat = function(name){ this.name = name; this.say = function(){ console.log("My name is "+this.name+" Nya Nyan") } } a_cat = new Cat("みー") a_cat.say() // => "My name is みー Nya Nyan"
これは、昔からあるjavascript の書き方で、今でも動くし、これで十分なことが多い。
ここでは、 コンストラクタ関数の定義、new 演算子、プロパティに値を代入 を行ってる
var Cat = function(name){ /*略*/ }; //コンストラクタ var a_cat = new Cat("みー");// new 演算子 a_cat.say() // => "My name is みー Nya Nyan"
JSON も new ??
JSON で書いたオブジェクトも new したくなる時があります。
var Dog = { name:"じろー", } var a_dog = new Dog(); //=>TypeError: object is not a function
これは、{} (= Object ) がで定義された変数はコンストラクタ関数ではなく、Objectのオブジェクト化可能な関数でもない(≠ new 可能なインスタンス)。JSONは Javascriptのオブジェクト記述のサブセットであり、コンストラクタ関数がなく、初期化できない
functionの中に this.XXX をいっぱい書くのは面倒だし、JSONでパパっと定義したいですよね
Object.create() の利用
JavaScript 1.8.5 / ECMAP 5th Edition からは、 Object.create() が使えるようになっている。
var Dog = { say:function(){ console.log( "My name is "+this.name+" Wan Wan") } } var a_animal = Object.create(Dog) a_animal.say(); //=>My name is undefined Wan Wan
安全にオブジェクトを生成することが出来る。 new 代替としても使える。this.name が存在しないので、undefined になってしまうけど。
JSON に name:"じろー"を含めた。
var Dog = { name:"じろー", // ←追加 say:function(){ console.log( "My name is "+this.name+" Wan Wan") } } var a_animal = Object.create(Dog) a_animal.say();//=>My name is undefined Wan Wan
できなくもないけど、 コンストラクタ関数がないので、 コンストラクタで引数を与えるということが出来ない。
では、「コンストラクタ引数に相当するもの」、これをどこに書けばいいのか?
これもできない。
this.name = name の部分をどこに書けばいいのか?
var Cat = function(name){ this.name = name; this.say = function(){ console.log("My name is "+this.name+" Nya Nyan") } } var a_cat = Object.create(Cat) a_cat.say();
apply 使うしか無いんだろうなぁ。とおもってMDN眺めてたら・・・
Object.createには、第二引数がある。
MDN によれば。。。Object.createには、第二引数がある。じゃぁこれを使えばいいんじゃないの?
構文
Object.create(proto [, propertiesObject ])
Object.create() に、第二引数をあたえよう。JSONをでいいのかな?
var Dog = { say:function(){ console.log( "My name is "+this.name+" Wan Wan") } } var a_animal = Object.create(Dog ,{name:"じろー"}) // ←JSON で与えてみた // =>TypeError: Property description must be an object
JSONで与えてみた結果。エラーになる。
ではどうすれば、第二引数で与えることが出来るのか。
Object.create の第二引数は形式が厳密に定義されている。
Object.create の第二引数は、厳密に定義されていて、その定義に従ったJSONオブジェクトしか受け付けないようだ
var Dog = { say:function(){ console.log( "My name is "+this.name+" Wan Wan") } } a_dog = Object.create(Dog,{ name:{ value: 'じろー' }, } ) a_dog.say()
MDNよると
{ "プロパティ名": { value : "値", writable : true, configurable : false, enumerable : false, get : function(){}, set : function(){}, } }
この規約に従うオブジェクトを渡さないといけない。
configurable
true である場合、この種のディスクリプタを変更することや、対応するオブジェクトからプロパティを削除することができます。既定値は false です。
enumerable
true である場合、このプロパティは対応するオブジェクトでのプロパティ列挙に現れます。既定値は false です。
value
プロパティに関連づけられた値です (データディスクリプタのみ)。既定値は undefined です。
writable
true である場合、プロパティに関連づけられた値は変更することができます (データディスクリプタのみ)。既定値は false です。
get
プロパティの getter として提供する関数、あるいは getter がない場合は undefined です (アクセサディスクリプタのみ)。既定値は undefined です。
set
プロパティの setter として提供する関数、あるいは setter がない場合は undefined です (アクセサディスクリプタのみ)。既定値は undefined です。
規定通りの値を与えてみる
writable = false したばあい
var Dog = { say:function(){ console.log( "My name is "+this.name+" Wan Wan") } } a_dog = Object.create(Dog,{ name:{ value: 'じろー',writable:false }, } ) a_dog.say() //=> My name is じろー Wan Wan a_dog.name = "たろー" a_dog.say() // => My name is じろー Wan Wan ←書き換わってない!
なるほど。getter/setter とプロパティの隠蔽や forEach 列挙を防止したりしてずいぶんと現代的になりました。ただアクセス権やGetter/Setterはあまりに作るのが面倒なので、いま手を出すのはちょっと辛いかも・・・
Object.create() を使ったオブジェクトの継承
なんだか、ずいぶんと面倒で、実用的ではないかもしれませんが。Object.create で拡張していくことも出来ます。
var Animal ={ voice : "", say:function(){ console.log( "My name is "+this.name+": "+ this.voice ) } } animal = Object.create(Animal) Dog = Object.create(Animal,{ voice:{value:"wan wan "} }) // A dog extends Animal Cat = Object.create(Animal,{ voice:{value:"nya nyan "} })// A Cat extends Animal a_dog = Object.create( Dog, {name : { value:"じろー"}} )// a dog extends Dog a_cat = Object.create( Cat, {name : { value:"みー"}} )// a cat extends Dog a_dog.say(); a_cat.say();
実行結果
My name is じろー: wan wan My name is みー: nya nyan
まとめ
Object.create() の第二引数は、 { "任意の名前": { value: "任意の値" } }
今回、敢えて触れなかったこと prototype と proto と this については敢えて触れずに済ませました。
参考資料
ECMA Script での定義
http://www.ecma-international.org/ecma-262/5.1/#sec-4.3.3 http://www.ecma-international.org/ecma-262/5.1/#sec-8.10.5
John Resig の記事
http://ejohn.org/blog/ecmascript-5-objects-and-properties/ http://ejohn.org/blog/ecmascript-5-objects-and-properties/
MDN での解説
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/create
その他
http://www.htmlgoodies.com/beyond/javascript/object.create-the-new-way-to-create-objects-in-javascript.html http://uhyohyohyo.sakura.ne.jp/javascript/9_3.html