16

私はそれをよりよく理解するためにjsでいくつかの継承を行ってきましたが、私を混乱させる何かを見つけました.

new キーワードを使用して「コンストラクター関数」を呼び出すと、その関数のプロトタイプへの参照を含む新しいオブジェクトが取得されることを私は知っています。

また、プロトタイプの継承を行うには、コンストラクター関数のプロトタイプを、「スーパークラス」にしたいオブジェクトのインスタンスに置き換える必要があることも知っています。

だから私はこれらの概念を試すためにこのばかげた例を作りました:

function Animal(){}
function Dog(){}

Animal.prototype.run = function(){alert("running...")};

Dog.prototype = new Animal(); 
Dog.prototype.bark = function(){alert("arf!")};

var fido = new Dog();
fido.bark() //ok
fido.run() //ok

console.log(Dog.prototype) // its an 'Object' 
console.log(fido.prototype) // UNDEFINED
console.log(fido.constructor.prototype == Dog.prototype) //this is true

function KillerDog(){};
KillerDog.prototype.deathBite = function(){alert("AAARFFF! *bite*")}

fido.prototype = new KillerDog();

console.log(fido.prototype) // no longer UNDEFINED
fido.deathBite(); // but this doesn't work!

(これは Firebug のコンソールで行われました)

1) すべての新しいオブジェクトにクリエーター関数のプロトタイプへの参照が含まれている場合、fido.prototype が定義されていないのはなぜですか?

2) 継承チェーンは [obj] -> [constructor] -> [prototype] ではなく [obj] -> [prototype] ですか?

3) オブジェクト (fido) の「プロトタイプ」プロパティはチェックされていますか? もしそうなら...なぜ「deathBite」は(最後の部分で)未定義なのですか?

4

5 に答える 5

12

1) すべての新しいオブジェクトにクリエーター関数のプロトタイプへの参照が含まれている場合、fido.prototype が定義されていないのはなぜですか?

すべての新しいオブジェクトは、構築時にコンストラクターに存在していたプロトタイプへの参照を保持します。ただし、この参照を格納するために使用されるプロパティ名prototypeは、コンストラクター関数自体にあるものとは異なります。__proto__一部の Javascript 実装では、他のプロパティ(Microsoft など) では許可されていないプロパティ名を介して、この「非表示」プロパティへのアクセスが許可されます。

2) 継承チェーンは [obj] -> [constructor] -> [prototype] ではなく [obj] -> [prototype] ですか?

いいえ、これを見てください:-

function Base() {}
Base.prototype.doThis = function() { alert("First"); }

function Base2() {}
Base2.prototype.doThis = function() { alert("Second"); }

function Derived() {}
Derived.prototype = new Base()

var x = new Derived()

Derived.prototype = new Base2()

x.doThis();

これは、2 番目ではなく「1 番目」に警告します。継承チェーンがコンストラクターを経由する場合、"Second" が表示されます。オブジェクトが構築されると、Functions プロトタイプ プロパティに保持されている現在の参照が、オブジェクトのプロトタイプへの非表示の参照に転送されます。

3) オブジェクト (fido) の「プロトタイプ」プロパティはチェックされていますか? もしそうなら...なぜ「deathBite」は(最後の部分で)未定義なのですか?

前述のように、呼び出されたプロパティをオブジェクト (関数以外) に割り当てることにprototypeは、特別な意味はありません。オブジェクトは、そのようなプロパティ名を介してそのプロトタイプへの参照を維持しません。

于 2008-12-24T18:13:59.207 に答える
7

でインスタンス化すると、オブジェクトのプロトタイプを変更することはできませんnew

上記の例では、次のような行

fido.prototype = new KillerDog();

prototypeobjectで名前が付けられた新しい属性を作成し、fidoその属性を新しいKillerDogオブジェクトに設定するだけです。と変わらない

fido.foo = new KillerDog();

あなたのコードが立っているように...

// Doesn't work because objects can't be changed via their constructors
fido.deathBite();

// Does work, because objects can be changed dynamically, 
// and Javascript won't complain when you use prototype 
//as an object attribute name
fido.prototype.deathBite();

特別なprototype動作は、javascript のコンストラクターにのみ適用されます。ここで、コンストラクターはfunctionで呼び出されるnewです。

于 2008-12-24T15:06:03.093 に答える
4

あなたの質問に数字で答えてください:

  1. オブジェクトのプロトタイプ プロパティが呼び出されていませんprototype。標準は[[prototype]]それを指定するために使用します。Firefox は、このプロパティを __proto__ という名前で公開しています。
  2. 継承チェーンは[obj][prototype object]. 元の仮定 ( [obj][constructor]⇒ ) は正しくなく、 and/orを変更し、どのメソッドを呼び出すことができるかを確認する[prototype]ことで簡単に反証できます— これらの変更は何も変更しないことがわかります。constructorconstructor.prototype[obj]
  3. prototypeオブジェクトのプロパティはチェックされず、使用されません。好きなように設定できます。JavaScript は、オブジェクトの構築中にのみ、関数オブジェクトでそれを使用します。

#3 を示すために、 Dojoのコードを次に示します。

dojo.delegate = dojo._delegate = (function(){
  // boodman/crockford delegation w/ cornford optimization
  function TMP(){}
  return function(obj, props){
    TMP.prototype = obj;
    var tmp = new TMP();
    if(props){
      dojo._mixin(tmp, props);
    }
    return tmp; // Object
  }
})();

ご覧のとおり、異なるプロトタイプを持つ委任されたすべてのオブジェクトに対してprototype同じ関数を再利用することで、1 つの場所でのみ使用されるという事実を利用しています。TMP実際prototypeには で関数を呼び出す前に直接割り当てられnew、その後変更され、作成されたオブジェクトには影響しません。

[[Prototype]] と JavaScript のプロトタイプの関係に対する私の回答で、オブジェクトが作成されたシーケンスを見つけることができます。

于 2008-12-24T18:19:10.443 に答える
2

ECMAScript 5 (つまり、JavaScript 言語の最新バージョン) では、次の方法でインスタンスの内部 [[Prototype]] プロパティにアクセスできることに注意してくださいObject.getPrototypeOf

Object.getPrototypeOf(fido) === Dog.prototype
于 2011-09-17T21:30:29.157 に答える
2

すでに回答されていることは知っていますが、継承を行うためのより良い方法があります。継承のためだけにコンストラクターを呼び出すことは望ましくありません。望ましくない影響の 1 つは次のとおりです。

function Base() {this.a = "A"}
function Child() {this.b = "B"};

Child.prototype = new Base();

これで、意図しないプロパティ "a" が Child のプロトタイプに追加されました。

これが正しい方法です (これは私が発明したものではありません。Ext-JS や他のライブラリはこれを使用しています)

// This is used to avoid calling a base class's constructor just to setup inheritance.
function SurrogateCtor() {}

/**
 * Sets a contructor to inherit from another constructor
 */
function extend(BaseCtor, DerivedCtor) {
  // Copy the prototype to the surrogate constructor
  SurrogateCtor.prototype = BaseCtor.prototype;
  // this sets up the inheritance chain
  DerivedCtor.prototype = new SurrogateCtor();
  // Fix the constructor property, otherwise it would point to the BaseCtor
  DerivedCtor.prototype.constructor = DerivedCtor;
  // Might as well add a property to the constructor to 
  // allow for simpler calling of base class's method
  DerivedCtor.superclass = BaseCtor;
}

function Base() {
  this.a = "A";
}

Base.prototype.getA = function() {return this.a}

function Derived() {
  Derived.superclass.call(this);  // No need to reference the base class by name
  this.b = "B";
}

extend(Base, Derived);
// Have to set methods on the prototype after the call to extend
// otherwise the prototype is overridden;
Derived.prototype.getB = function(){return this.b};
var obj = new Derived();

さらに簡単な方法は、extend を呼び出してプロトタイプにメソッドを追加する必要がないように、派生クラスのメソッドを指定する場所に 3 番目のパラメータを追加することです。

extend(BaseCtor, DerivedCtor, {
  getB: function() {return this.b}
});

次に、シンタックス シュガーに対してできることは他にもたくさんあります。

それについてのブログ: http://js-bits.blogspot.com/2010/08/javascript-inheritance-done-right.html

于 2010-07-30T23:00:41.763 に答える