2011-11-11 15 views
35

次のコードを考慮してください。コンストラクタは、b、cのために更新されないのはなぜJavaScriptの継承とコンストラクタのプロパティ

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 
  • instanceofnew c()cのインスタンスであることを知っているためにどのようにそれは可能ですが、(new c()).constructora()に等しく、Object.getPrototypeOf(new c())a{ }であることを考えると?デフォルトでは

http://jsfiddle.net/ezZr5/

+3

コンストラクタを更新する必要がある理由は何ですか?私は、その財産が存在しないふりをすれば、私の人生が簡単だとわかります。 – hugomg

+0

私はこれを重複して閉じるのに苦労しています - 他のすべてのクエストはとても冗長です... – hugomg

+3

'c.prototype'は' b() 'で、' b.prototype'は 'a()'です。 'c.prototype'は' a() ' –

答えて

63

さて、少し心のゲームをプレイしてみましょう:

上の画像から、我々は見ることができます:

  1. 我々はfunction Foo() {}のような関数を作成し、JavaScriptはFunctionインスタンスを作成します。
  2. すべてFunctionインスタンス(コンストラクタ関数)は、ポインタであるプロパティprototypeを持ちます。
  3. コンストラクタ関数のprototypeプロパティは、そのプロトタイプオブジェクトを指します。
  4. プロトタイプオブジェクトには、ポインタでもあるプロパティconstructorがあります。
  5. プロトタイプオブジェクトのconstructorプロパティは、そのコンストラクタ関数を指します。
  6. 新しいインスタンスFoonew Foo()のように作成すると、JavaScriptは新しいオブジェクトを作成します。
  7. インスタンスの内部[[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プロパティを定義すると、次の理由で有利である:

  1. それは論理的に正しいです。たとえば、Object.prototypeと考えてください。 constructorプロパティがObject.prototypeの場合、Objectを示します。インスタンスにconstructorプロパティが定義されている場合、は、nullのインスタンスなので、はundefinedとなります。
  2. これは、他のプロトタイプの方法とまったく同じように扱われます。これにより、すべてのインスタンスでconstructorプロパティを定義する必要がないため、newのジョブが簡単になります。
  3. すべてのインスタンスは同じconstructorプロパティを共有します。したがって効率的です。

我々が継承について話すとき今、私たちは次のシナリオを持っている:

我々が見ることができる上記の画像から:派生コンストラクタのprototypeプロパティに設定されて

  1. 基本コンストラクタのインスタンス
  2. したがって、導出されたコンストラクタのインスタンスの内部の[[proto]]プロパティもそれを指しています。
  3. したがって、派生コンストラクタインスタンスのconstructorプロパティは、ベースコンストラクタを指すようになりました。それは、インスタンスのconstructor性質に依存しない人気の信念に反してinstanceofオペレータ、用として

。上記からわかるように、それは誤った結果につながります。

instanceof演算子は2項演算子です(2つのオペランドを持ちます)。インスタンスオブジェクトとコンストラクタ関数で動作します。 Mozilla Developer Networkに説明したように、単純に次のようん:

  1. foo.__proto__ === Foo.prototype
  2. 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 
    } 
    

    単にBarからFoo継承する場合、そのFooのインスタンスのプロトタイプチェーンは次のようになり、それを置くために、 foo.__proto__.__proto__ === Bar.prototype

  3. foo.__proto__.__proto__.__proto__ === Object.prototype
  4. foo.__proto__.__proto__.__proto__.__proto__ === null

ご覧のとおり、すべてのオブジェクトはObjectコンストラクタを継承しています。プロトタイプチェーンは、内部[[proto]]プロパティがnullを指しているときに終了します。

instanceof機能は、単にインスタンス・オブジェクト(最初のオペランド)のプロトタイプチェーンを横断し、コンストラクタ関数(第2オペランド)のprototypeプロパティに、各オブジェクトの内部[[proto]]性を比較します。一致すると、trueを返します。プロトタイプチェーンが終了する場合は、falseを返します。

+4

http://joost.zeekat.nl/constructors-considered-mildly-confusing.html – Christoph

+3

+1私は '.__ proto__'の代わりに' Object.getPrototypeOf'を好むでしょう。 – pimvdb

+3

私は、説明のために '__proto__'プロパティを使用しました。それがMozilla Developer Networkで説明された方法です。それにもかかわらず、 '__proto__'プロパティは' Object.getPrototypeOf'メソッドよりも高速です(関数呼び出しのオーバーヘッドなし)、それはすべての主要なブラウザで実装されています。 'Object.getPrototypeOf'を使う唯一の理由は、' __proto__'プロパティをサポートしていないRhinoのような実装を回避することです。私たちは皆自分の好みを持っています。より読みやすいので '__proto__'プロパティが好きです。乾杯。 =) –

12

、その後、

function b() {} 

b.prototypeは自動的にbに設定されている.constructor性質を持っています。ただし、現在のプロトタイプを上書きするので、その変数を破棄している:

b.prototype = new a; 

はその後b.prototypeもう.constructor性質を持っていません。上書きで消去されました。 aからを継承しますが、(new a).constructor === aであり、したがって(new b).constructor === a(プロトタイプチェーンの同じプロパティを参照しています)です。行うための最善の

単に後でそれを手動で設定することです:

b.prototype.constructor = b; 

ます。また、このために少し機能を作ることができる:

function inherit(what, from) { 
    what.prototype = new from; 
    what.prototype.constructor = what; 
} 

http://jsfiddle.net/79xTg/5/

+1

それはすべて本当です。しかし、特定のオブジェクトが継承されていることはわかりません。 'new b()instanceof a'は、継承をチェックする必要がある場合にOPの場合に望ましくない可能性のある' $ .extend'を使用すると 'false'を返します。 –

+0

@Robert Koritnik:良い点ですが、 'b instanceof b'はコンストラクタがそれ自身のインスタンスではないので常にfalseです。拡張機能は、あなたが誤解していないかぎり、 'new b instanceof b'でも動作するようです:http://jsfiddle.net/79xTg/3/。 – pimvdb

+0

私のコメントを編集しました。もちろん、タイプミスでした。あなたの例を編集してみましょう:http://jsfiddle.net/79xTg/4/ '$ .extend'は継承をしませんが、' prototype'コピーです。したがって、それを使って継承をチェックすることはできません。 –

4

constructorは規則的で、非functionオブジェクトのprototypeプロパティのデフォルト値の-enumerableプロパティ。したがって、prototypeに割り当てるとプロパティが失われます。

instanceofはまだそれがconstructorを使用していないとして働くのではなく、機能のprototypeプロパティの(現在の)値のオブジェクトのプロトタイプチェーンをスキャンし、すなわちfoo instanceof FooはECMAScript3で

var proto = Object.getPrototypeOf(foo); 
for(; proto !== null; proto = Object.getPrototypeOf(proto)) { 
    if(proto === Foo.prototype) 
     return true; 
} 
return false; 

に相当しますユーザ定義のプロパティが常に列挙可能である(つまり、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で立ち往生している場合は、代わりにObject.create()のカスタムclone() functionを使用する必要がありますし、することができませんconstructor非列挙します

Cat.prototype = clone(Pet.prototype); 
Cat.prototype.constructor = Cat; 
関連する問題