それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

Javascriptにおける、Object.createと new の間のオブジェクト継承

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 可能なインスタンス)。JSONJavascriptのオブジェクト記述のサブセットであり、コンストラクタ関数がなく、初期化できない

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