constructor,prototypeなど動作確認

Tag:

JavaScriptでオブジェクト指向プログラミングをするにあたって、基礎知識を確認するためのメモです。

コンストラクタでインスタンスを生成
var Person = function(name) {
    var tmp = 20;
    this.name = name;
};

var p1 = new Person("鈴木");   
console.log(p1.name);          // 鈴木
console.log(p1.tmp);           // undefined 

まず、functionを通じて、インスタンスを生成するためのFunctionオブジェクト(コンストラクタ)を定義します。ここでは、Personコンストラクタを定義してます。コンストラクタの名前は大文字で始めるのが一般的です。

次に、コンストラクタをnew演算子で呼び出し、インスタンスを生成します。

コンストラクタ配下内で「this.プロパティ名」と定義することで、インスタンスのプロパティを設定できます。コンストラクタ配下内で「this.name」と定義しているためインスタンスにnameプロパティが設定されています。

当たり前ですが、コンストラクタ配下内で定義しているtmp変数は単なるPersonコンストラクタ内でのローカル変数のため、インスタンスには設定されません。

prototypeプロパティって何?
var Person = function() {};   
Person.prototype.name = "鈴木";
Person.prototype.chgName = function(name) {
    Person.prototype.name = name;
};

var p1 = new Person();         // ★1
var p2 = new Person();         //
console.log(p1.name);          // 鈴木
console.log(p2.name);          // 鈴木

p1.name = '山田';              // ★2
console.log(p1.name);          // 山田
console.log(p2.name);          // 鈴木

p2.chgName('佐藤');            // ★3
console.log(p1.name);          // 山田
console.log(p2.name);          // 佐藤 

delete p1.name;                // ★4
console.log(p1.name);          // 佐藤
console.log(p2.name);          // 佐藤

prototypeプロパティは、関数(Functionオブジェクト)が生成されたときに自動的に作られるプロパティで、constructorプロパティのみをもつオブジェクトを参照しています。prototypeプロパティが参照するオブジェクトに設定したメンバは、インスタンス化されたオブジェクトで暗黙的に参照されます。

★1では、p1インスタンス、p2インスタンスともにnameプロパティを設定していませんがnameプロパティにアクセスすると『鈴木』と表示されます。インスタンス化されたオブジェクトでプロパティが設定されていない場合、prototypeオブジェクトのプロパティを参照するためです。

★2では、p1インスタンスのnameプロパティを設定してます。設定を行うと、p1インスタンス自身がnameプロパティを持つようになるのでprototypeオブジェクトのnameプロパティを参照しなくなります。

★3では、prototypeオブジェクトのnameプロパティの値を書き換えてます。p2インスタンスはprototypeオブジェクトのnameプロパティを参照するため『佐藤』と表示されるようになりました。

★4では、delete演算子でp1インスタンスのnameプロパティを削除してます。その後、p1インスタンスでnameプロパティにアクセスすると『佐藤』と表示されるようになりました。これはp1インスタンスでnameプロパティを持たなくなり、prototypeオブジェクトのnameプロパティを参照するようになったためです。

クラスの継承
var Xxx = function() {console.log("Xxx create");};
Xxx.prototype.name1 = 'xxx1';
Xxx.prototype.name2 = 'xxx2';
Xxx.prototype.name3 = 'xxx3';
Xxx.prototype.fX = function() {console.log("fX");};
var x =  new Xxx();                                  // Xxx create 
console.log(Xxx.prototype);                          // [object Object]
console.log(Xxx.prototype.constructor);              // function() {console.log("Xxx create");} 
console.log(x.name1);                                // xxx1
console.log(x.name2);                                // xxx2
console.log(x.name3);                                // xxx3

var Yyy = function() {console.log("Yyy create");};
Yyy.prototype = new Xxx();                           // Xxx create        ★1 
Yyy.prototype.name2 = 'yyy2';
Yyy.prototype.name3 = 'yyy3';
Yyy.prototype.fY = function() {console.log("fY");};
var y =  new Yyy();                                  // Yyy create
console.log(Yyy.prototype);                          // [object Object] 
console.log(Yyy.prototype.constructor);              // function() {console.log("Xxx create");} 
console.log(y.name1);                                // xxx1
console.log(y.name2);                                // yyy2 
console.log(y.name3);                                // yyy3 

var Zzz = function() {console.log("Zzz create");};
Zzz.prototype = new Yyy();                           // Yyy create        ★2
Zzz.prototype.name3 = 'zzz3';
Zzz.prototype.fZ = function() {console.log("fZ");};
var z =  new Zzz();                                  // Zzz create 
console.log(Zzz.prototype);                          // [object Object] 
console.log(Zzz.prototype.constructor);              // function() {console.log("Xxx create");} 
console.log(z.name1);                                // xxx1
console.log(z.name2);                                // yyy2
console.log(z.name3);                                // zzz3

console.log(z.constructor);                          // function() {console.log("Xxx create");} 
console.log(z.constructor.prototype);                // [object Object]  
console.log(z.constructor.prototype.name3);          // xxx3              ★3

子クラスのprototypeプロパティに、親クラスのインスタンスを設定することでクラスが継承されます。★1では、XxxクラスをYyyクラスが継承し、★2では、YyyクラスをZzzクラスが継承してます。

★3では、Xxx.prototype.name3 を参照しているため『xxx3』と表示されます。

メンバの列挙
for in文を使って、オブジェクト内に存在するプロパティを取り出します。不必要なプロパティを取り除くには、typeof演算子hasOwnPropertyメソッドを利用します。hasOwnPropertyメソッドは、オブジェクト自身にプロパティがあるかどうかを判断します。

var Xxx = function() {console.log("Xxx create");};
Xxx.prototype.name1 = 'xxx1';
Xxx.prototype.name2 = 'xxx2';
Xxx.prototype.name3 = 'xxx3';
Xxx.prototype.fX = function() {console.log("fX");};
var x =  new Xxx();                                  // Xxx create 

var Yyy = function() {console.log("Yyy create");};
Yyy.prototype = new Xxx();                           // Xxx create
Yyy.prototype.name2 = 'yyy2';
Yyy.prototype.name3 = 'yyy3';
Yyy.prototype.fY = function() {console.log("fY");};
var y =  new Yyy();                                  // Yyy create

var Zzz = function() {console.log("Zzz create");};
Zzz.prototype = new Yyy();                           // Yyy create
Zzz.prototype.name3 = 'zzz3';
Zzz.prototype.fZ = function() {console.log("fZ");};
var z =  new Zzz();                                  // Zzz create 
z.name4 = 'zzz4';



//### プロパティのみを列挙 ###
tmp = "";
for (name in z) {
    if (typeof z[name] !== 'function') {
        tmp += name + ':' + z[name] + '    ';  
    }
}
console.log(tmp);                                    
// name3:zzz3    name2:yyy2    name1:xxx1 



//### メソッドのみを列挙 ###
tmp = "";
for (name in z) {
    if (typeof z[name] === 'function') {
        tmp += name + ':' + z[name] + '    ';  
    }
}
console.log(tmp);
// fZ:function() {console.log("fZ");}    fY:function() {console.log("fY");}    fX:function() {console.log("fX");} 



//### zインスタンスがもつプロパティのみを列挙 ###
tmp = "";
for (name in z) {
    if (z.hasOwnProperty(name)) {
        tmp += name + ':' + z[name] + '    ';  
    }
}
console.log(tmp); 
// name4:zzz4  

スポンサーリンク