javascript继承问题

selfcontroller 2013-11-24
最近在研究javascript继承问题,遇到点疑惑,希望大家指点,具体看代码:
function Dog(){
  this.tail = true;
}

dog1 = new Dog();
dog2 = new Dog();

Dog.prototype.say = function(){
  return 'woof';
}

dog1.say();
>>> 'woof'

dog2.say();
>>> 'woof';


以上代码说明,只要我们在构造器的原型对象上添加了新的方法或者属性,那么不管实例是什么时候创建的,他们都能访问这些新添加的方法或者属性。并且我们访问dog1和dog2的constructor的时候返回的都是Dog():
dog1.constructor;
>>> Dog()
dog2.constructor:
>>> Dog()


通过以上的代码,你会感觉一切正常,但是如果我们访问的是该原型对象的构造器的话,返回的也是Dog(),这就不对了,因为这个时候,它的原型对象应该是一个有Object()创建的一个一般对象才对,并不拥有Dog()所构造的对象所拥有的属性。按理说,每个实例的constructor属性默认调用的是prototype的constructor的属性,这是我迷惑的地方之一,希望各位指教。

另一个疑惑:
继续上面的例子,这次不同的是,我用一个自定义的对象覆盖了原有的原型对象:
  Dog.prototype = {paws:4,hair:true}
  typeof(dog1.paws)
  >>> 'undefined'
  typeof(dog2.paws)
  >>> 'undefined'
  dog1.say();
  >>> 'woof'

  dog3 = new Dog();
  dog3.say()
  >>> TypeError: dog3.say is not a function
  dog3.paws
  >>> 4


    从上面的代码可以看出,在我用自定义的对象覆盖了之前的原型对象之后,dog1和dog2,不能再访问我新添加的属性(paws,hair)了,这说明,在dog1和dog2看来,自定义的对象所充当的原型对象已经不是他们当初的原型对象了,然而他们依然能访问我改变之前的原型对象,这是我疑惑的地方之二,请问,他们是如何知道原型对象发生改变的?又是如何找到之前的原型对象的?
   对于以上疑问,我通过查阅资料得知,每个实例都存在一个指向相关原型的链接,叫__proto__ 也就是说,dog1和dog2是通过这个属性来找到他们的原型对象的,而不是通过dog1.constructor.prototype。并且,即使constructor的prototype已经被改变(我后来用自定义的对象覆盖了它),dog1和dog2的 __proto__ 属性是不会跟着改变的,这也就解释了为什么我改变了constructor的prototype之后,dog1和dog2依然能够访问say方法了。而且,每个实例对象的__proto__属性是由该实例对象被实例化时的constructor的prototype所决定的。当然这只是我的个人理解,不知是否正确,请各位指点。

迷惑3:
继续上面的例子,因为我用自定义的对象覆盖了Dog()的默认prototype,这个时候我访问dog3.constructor:
  dog3.constructor
  >>> Object()
  dog1.constructor
  >>> Dog()

通过上面的代码你会发现,其实每个实例也有一个constructor属性,并且,这个constructor的属性默认是调用prototype的constructor属性的,所以你看见dog3的constructor跟dog1的constructor已经不一样了。但是这个结论刚好又跟我开始说的第一个疑惑相冲突,行开始的例子你能发现,dog1的constructor是Dog(),而我们的推测却是该dog1的prototype的constructor刚好是个Object(),难道是我的推测错了么?这刚好是我的第三个疑问,请各位多多指教。
jackyin5918 2013-11-25
下面代码,貌似涉及到 第三个疑惑,
供参考
http://jackyin5918.iteye.com/admin/blogs/1881246
//定义一个基类
  function Animal()
  {
    this.species = "动物"; 
  }
  
  Animal.prototype.show = function() { alert('This is An Animal. name=' + this.name);}; //定义基类一个普通方法
  
  Animal.legCount=4; //定义动物有4条腿,类的静态属性
  Animal.TestStaticFunction = function() {alert('TestStaticFunction.');};//基类,静态方法
  
  //定义一个子类
  function Cat(name,color)
  {
     Animal.call(this);//相当于调用基类的构造函数,将基类的属性继承过来,但仅限于在基类的构造函数中定义的属性
     this.name = name;
     this.color = color;
  } 
  
  var EmptyObject = function(){}; //
 EmptyObject.prototype = Animal.prototype;
 Cat.prototype = new EmptyObject();  //修改Cat的prototype不会影响到基类的prototype
  Cat.prototype.constructor = Cat;    //因为每个类的prototype对象都有个constructor属性,指向类自己,
  //这里需要Cat.prototype其实是Animal,而Animal的constructor是指向Animal的,
  //造成了Cat的constructor指向了Animal,产生混乱,需要修正过来

  var anAnimal = new Animal();
  var aCat = new Cat('Tom','black');
  
  console.log(aCat); 
  anAnimal.show(); //基类prototype中方法,正常访问,(但基类中this.name未定义)
  aCat.show();     //正常,通过prototype方式继承了基类prototype中的方法
selfcontroller 2013-11-25
@jackyin5918,这。。。。。
jackyin5918 2013-11-26
selfcontroller 写道
@jackyin5918,这。。。。。

这。。。。。 是什么意思?

通过以上的代码,你会感觉一切正常,但是如果我们访问的是该原型对象的构造器的话,返回的也是Dog(),这就不对了,因为这个时候,它的原型对象应该是一个有Object()创建的一个一般对象才对,并不拥有Dog()所构造的对象所拥有的属性。

这个疑问从何而来?

prototype 是每个function都有的,在定义function的时候,会给这个function增加一个prototype属性,其中这个prototype又有一个constructor属性,指向这个function本身.
在定义好这个function,使用new创建出来的对象实例会拷贝原来function的prototype为自己的__proto__属性.所以访问对象实例的constructor,就是原来函数(构造器)的constructor.

它的原型对象应该是一个有Object()创建的一个一般对象才对

这个结论 怎么得出来的?

貌似楼主对prototype这个没搞清楚吧.

另,
在new()的时候,js主要干了下面几件事情:
 
  1. js运行环境首先创建一个空对象
  2. 把this变量指向这个对象
  3. 把__proto__指向这个构造器的prototype属性
  4. 通过this把属性和方法加在这个对象上
  5. 最后会把this指向的对象return出来(当然你也可以显示的return别的对象,这是情况跟我讨论的有点不一样)
参考: http://jackyin5918.iteye.com/admin/blogs/1881650
selfcontroller 2013-11-26

@jackyin5918 ,谢谢你,我想我明白了,你的解释很透彻,之前一直以为每个函数的prototype默认都是一个空对象,并且他们的constructor都是Object的,经你这么一说,确实是有道理的,谢谢你。
lt0604 2013-11-29
Dog.prototype应该是赋值语句,而不是重写。
Global site tag (gtag.js) - Google Analytics