次のコードについて考えてみます。
_function a() {}
function b() {}
function c() {}
b.prototype = new a();
c.prototype = new b();
console.log((new a()).constructor); //a()
console.log((new b()).constructor); //a()
console.log((new c()).constructor); //a()
_
また、以下の点を考慮してください。
_console.log(new a() instanceof a); //true
console.log(new b() instanceof b); //true
console.log(new c() instanceof c); //true
_
(new c()).constructor
_がa()
と等しく、Object.getPrototypeOf(new c())
が_a{ }
_であるとすると、instanceof
がnew c()
はc
のインスタンスですか?さて、ちょっとマインドゲームをしましょう:
上の画像から、次のことがわかります。
function Foo() {}
のような関数を作成すると、JavaScriptはFunction
インスタンスを作成します。Function
インスタンス(コンストラクター関数)には、ポインターであるプロパティprototype
があります。prototype
プロパティは、そのプロトタイプオブジェクトを指します。constructor
があります。constructor
プロパティは、そのコンストラクター関数を指します。new Foo()
のようにFoo
の新しいインスタンスを作成すると、JavaScriptは新しいオブジェクトを作成します。[[proto]]
プロパティは、コンストラクターのプロトタイプを指します。ここで、JavaScriptがプロトタイプではなくインスタンスオブジェクトにconstructor
プロパティをアタッチしないのはなぜかという疑問が生じます。考えてみましょう:
function defclass(prototype) {
var constructor = prototype.constructor;
constructor.prototype = prototype;
return constructor;
}
var Square = defclass({
constructor: function (side) {
this.side = side;
},
area: function () {
return this.side * this.side;
}
});
var square = new Square(10);
alert(square.area()); // 100
ご覧のとおり、constructor
プロパティは、上記の例のarea
のように、プロトタイプの単なる別のメソッドです。 constructor
プロパティを特別なものにしているのは、プロトタイプのインスタンスを初期化するために使用されることです。それ以外は、プロトタイプの他の方法とまったく同じです。
プロトタイプでconstructor
プロパティを定義すると、次の理由で有利になります。
Object.prototype
について考えてみます。 Object.prototype
のconstructor
プロパティはObject
を指します。 constructor
プロパティがインスタンスで定義されている場合、Object.prototype.constructor
はundefined
のインスタンスであるため、Object.prototype
はnull
になります。new
プロパティを定義する必要がなくなるため、constructor
の作業が簡単になります。constructor
プロパティを共有します。したがって、それは効率的です。継承について話すとき、次のシナリオがあります。
上の画像から、次のことがわかります。
prototype
プロパティは、基本コンストラクターのインスタンスに設定されます。[[proto]]
プロパティもそれを指します。constructor
プロパティは、ベースコンストラクターを指すようになりました。instanceof
演算子に関しては、一般的な考えに反して、インスタンスのconstructor
プロパティに依存しません。上からわかるように、それは誤った結果につながるでしょう。
instanceof
演算子は二項演算子です(2つのオペランドがあります)。インスタンスオブジェクトとコンストラクター関数を操作します。 Mozilla Developer Network で説明されているように、それは単に次のことを行います。
function instanceOf(object, constructor) {
while (object != null) {
if (object == constructor.prototype) { //object is instanceof constructor
return true;
} else if (typeof object == 'xml') { //workaround for XML objects
return constructor.prototype == XML.prototype;
}
object = object.__proto__; //traverse the prototype chain
}
return false; //object is not instanceof constructor
}
簡単に言うと、Foo
がBar
から継承する場合、Foo
のインスタンスのプロトタイプチェーンは次のようになります。
foo.__proto__ === Foo.prototype
foo.__proto__.__proto__ === Bar.prototype
foo.__proto__.__proto__.__proto__ === Object.prototype
foo.__proto__.__proto__.__proto__.__proto__ === null
ご覧のとおり、すべてのオブジェクトはObject
コンストラクターから継承しています。プロトタイプチェーンは、内部の[[proto]]
プロパティがnull
を指すと終了します。
instanceof
関数は、インスタンスオブジェクト(最初のオペランド)のプロトタイプチェーンをトラバースし、各オブジェクトの内部[[proto]]
プロパティをコンストラクター関数のprototype
プロパティ(第2オペランド)。それらが一致する場合、true
を返します。それ以外の場合、プロトタイプチェーンが終了すると、false
が返されます。
デフォルトでは、
function b() {}
その場合、b.prototype
には.constructor
プロパティがあり、これは自動的にb
に設定されます。ただし、現在、プロトタイプを上書きしているため、その変数を破棄しています。
b.prototype = new a;
その場合、b.prototype
には.constructor
プロパティがなくなります。上書きで消去されました。 doesa
から継承し、(new a).constructor === a
、したがって(new b).constructor === a
(プロトタイプチェーン内の同じプロパティを参照しています)。
後で手動で設定するのが最善の方法です。
b.prototype.constructor = b;
このための小さな関数を作成することもできます。
function inherit(what, from) {
what.prototype = new from;
what.prototype.constructor = what;
}
constructor
は、関数オブジェクトのprototype
プロパティのデフォルト値の通常の列挙不可能なプロパティです。したがって、prototype
に割り当てると、プロパティが失われます。
instanceof
は、constructor
を使用しないため、引き続き機能しますが、オブジェクトのプロトタイプチェーンをスキャンして、関数のprototype
プロパティの(現在の)値を探します。つまり、_foo instanceof Foo
_はと同等です。
_var proto = Object.getPrototypeOf(foo);
for(; proto !== null; proto = Object.getPrototypeOf(proto)) {
if(proto === Foo.prototype)
return true;
}
return false;
_
ECMAScript3では、ユーザー定義のプロパティは常に列挙可能であるため(つまり、_for..in
_に表示されるため)、組み込みのプロパティと同じように動作するconstructor
プロパティを設定する方法はありません。
これはECMAScript5で変更されました。ただし、constructor
を手動で設定した場合でも、コードにはまだ問題があります。特に、prototype
を親 'クラス'のインスタンスに設定することはお勧めできません。子クラスの場合は親コンストラクターを呼び出さないでください。 'が定義されますが、子インスタンスが作成されるときです。
これは、それがどのように行われるべきかについてのいくつかのECMAScript5サンプルコードです:
_function Pet(name) {
this.name = name;
}
Pet.prototype.feed = function(food) {
return this.name + ' ate ' + food + '.';
};
function Cat(name) {
Pet.call(this, name);
}
Cat.prototype = Object.create(Pet.prototype, {
constructor : {
value : Cat,
writable : true,
enumerable : false,
configurable : true
}
});
Cat.prototype.caress = function() {
return this.name + ' purrs.';
};
_
ECMAScript3で立ち往生している場合は、clone()
の代わりにカスタム Object.create()
function を使用する必要があり、constructor
nonを作成できません。 -列挙可能:
_Cat.prototype = clone(Pet.prototype);
Cat.prototype.constructor = Cat;
_