Javascript原型继承?

Javascript原型继承?,javascript,inheritance,prototype,prototypal-inheritance,Javascript,Inheritance,Prototype,Prototypal Inheritance,为了更好地理解js,我一直在做一些继承工作,我发现了一些让我困惑的东西 我知道当你用new关键字调用一个“constructor function”时,你会得到一个引用该函数原型的新对象 我还知道,为了进行原型继承,必须将构造函数的原型替换为希望成为“超类”的对象的实例 所以我做了这个愚蠢的例子来尝试这些概念: function Animal(){} function Dog(){} Animal.prototype.run = function(){alert("running...")};

为了更好地理解js,我一直在做一些继承工作,我发现了一些让我困惑的东西

我知道当你用new关键字调用一个“constructor function”时,你会得到一个引用该函数原型的新对象

我还知道,为了进行原型继承,必须将构造函数的原型替换为希望成为“超类”的对象的实例

所以我做了这个愚蠢的例子来尝试这些概念:

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)的“原型”属性?如果是的话。。。为什么“死亡之咬”没有定义(在最后一部分)?

一旦用
new
实例化对象,就不能更改其原型

在上面的示例中,行如下

fido.prototype = new KillerDog();
只需在对象
fido
上创建名为
prototype
的新属性,并将该属性设置为新的
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中的构造函数,其中构造函数是
函数
s,将使用
new
调用

1) 为什么所有新对象都包含 引用创建者函数的 原型,fido。原型是 未定义

所有新对象都包含对其构造函数在构建时出现的原型的引用。但是,用于存储此引用的属性名不是
prototype
,而是构造函数本身的属性名。一些Javascript实现确实允许通过一些属性名称(如
\uuuuu proto\uuuu
)访问此“隐藏”属性,而其他实现则不允许(例如Microsofts)

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();
这会提醒“第一个”,而不是第二个。如果继承链经过构造函数,我们将看到“第二个”。构造对象时,“函数原型”属性中的当前引用将被传递到对象的隐藏引用,该对象隐藏到其原型中

3) 是我们产品的“原型”属性吗 对象(fido)是否检查过?如果是的话。。。 为什么“致命伤”没有定义(在 最后一部分


将名为
原型的属性指定给对象(函数除外)没有特殊意义,如前所述,对象不会通过这样的属性名称维护对其原型的引用。

请用数字回答您的问题:

  • 对象的prototype属性未调用
    prototype
    。标准使用
    [[prototype]]
    来指定它。Firefox以_proto__的名义将此属性公开
  • 继承链是
    [obj]
    ⇒ <代码>[原型对象]
  • 。您最初的假设(
    [obj]
    ⇒ <代码>[构造函数]⇒
    [prototype]
    )不正确,您可以通过修改
    构造函数和/或
    构造函数来轻松反驳它。prototype
    ,并检查可以对您的
    [obj]
    调用哪些方法-您会发现这些修改不会改变任何东西
  • prototype
    对象上的属性未被检查和使用。你可以把它设置成你喜欢的任何东西。JavaScript仅在对象构造期间在函数对象上使用它
  • 为了演示#3,以下代码来自:

    正如您所见,它利用了这样一个事实,即
    prototype
    仅在一个地方使用,即对具有不同原型的所有委托对象重用相同的函数
    TMP
    。事实上,
    prototype
    是在使用
    new
    调用函数之前直接分配的,之后将进行更改,不会影响任何创建的对象


    您可以在我的答案中找到创建的对象序列。

    我知道它已经被回答了,但是,有更好的方法进行继承。仅仅为了继承而调用构造函数是不可取的。其中一个不希望出现的影响是

    function Base() {this.a = "A"}
    function Child() {this.b = "B"};
    
    Child.prototype = new Base();
    
    现在,您已经将属性“a”添加到您不打算添加的Child原型中

    这是正确的方法(这不是我发明的,ExtJS和其他LIB使用这个)

    更简单的方法是在指定派生类的方法的位置添加第三个参数进行扩展,这样就不必调用extend然后向原型添加方法

    extend(BaseCtor, DerivedCtor, {
      getB: function() {return this.b}
    });
    
    还有很多其他的事情你可以做的语法糖


    关于它的博客:

    值得注意的是,在ECMAScript 5(即JavaScript语言的最新版本)中,您可以通过以下方式访问实例的内部[[Prototype]]属性:


    美好的请注意,如果在Derived.prototype=new Base2()之后声明var x=new-Derived(),则会看到“Second”。x只是持有一个指向原始派生.prototype的指针,但这并不意味着我们实际上没有将其重定向到Base2()。指针只是被改变了。根本不反驳安东尼说的任何话,只是澄清了最后一点。看我的例子:哦,我想你的意思是:原型而不是原型Markdown,呃!下划线proto下划线asker缺少的一点是prototype属性只存在于构造函数中,而不存在于对象的实例中。因此,如果您有一个实例,并且希望了解其原型,则必须调用instance.constructor.prototype。但是,如前所述,设置不会改变实例的原型,只会改变构造函数的原型。您可以在Mozilla(Firefox等)中进行设置。但这通常是不切实际的。
    // 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(BaseCtor, DerivedCtor, {
      getB: function() {return this.b}
    });
    
    Object.getPrototypeOf(fido) === Dog.prototype