彻底理解Javascript原型继承
之前写过一篇Javascript继承主题的文章,这篇文章作为一篇读书笔记,分析的不够深入。
本文试图进一步思考,争取彻底理解Javascript继承原理
实例成员与原型成员
举一个《高性能Javascript》书中例子
var book={ title :"High Performance JavaScript", publisher:"Yahoo!Press" }; alert(book.toString());//"[object Object]"
Javascript对象有两种类型的成员:实例成员和原型成员。实例成员存在于实例自身,原型成员则从对象的原型继承
上述示例中,book对象有两个实例成员:title和publisher。book对象并没有定义toString()方法,但是却可以调用这一方法,这是因为toString()方法是book对象继承的一个原型成员
book示例成员与原型成员关系如下图。当book.toString()被调用时,首先从对象实例搜索toString方法,如果没有找到,就转向搜索原型对象。通过这种方式,book可以访问他的原型所有的属性和方法。
原型模式创建对象
function Person(){} Person.prototype.name="Nicholas"; Person.prototype.age=29; Person.prototype.job="Software Engineer"; Person.prototype.sayName=function(){alert(this.name)}; var person1=new Person(); person1.sayName();//"Nicholas" var person2=new Person(); person2.sayName();//"Nicholas"
上述代码中,Person构造函数、原型对象和实例对象见关系如下
将Person属性成员不再定义在原型对象上时
function Person(name) { this.name=name; } Person.prototype.sayName=function(){alert(this.name)}; var p1=new Person("Zhangsan");
上述代码中,Person构造函数、原型对象和实例对象关系如下图所示
从上面两图可以看出,当属性定义在构造函数中时,属性成员存在于实例对象中;属性定义在原型对象中时,实例对象访问这一属性,需要沿原型链查找该属性
原型链继承
让构造函数的原型对象等于另一个类型的实例,利用原型让一个引用类型继承另一个引用类型的属性和方法
function SuperType() { this.property=true; } SuperType.prototype.getSuperValue=function(){ return this.property; }; function SubType() { this.subProperty=false; } //继承SuperType SubType.prototype=new SuperType(); SubType.prototype.getSubValue=function(){ return this.subProperty; } var instance=new SubType(); alert(instance.getSuperValue());//true
代码示例中,完整原型链如下
图中值得注意的细节是,父类SuperType的属性property存在于SuperType的实例对象中,getSuperValue方法存在于其原型对象中
将SuperType实例对象赋给子类SubType的原型对象后,SubType原型对象prototype获得了父类的property属性
换一种写法行不行?
上文是通过将父类的实例对象赋给子类的原型对象,实现了继承的效果
直接将父类的原型对象赋给子类的原型对象行不行?
function Super() { this.attr='a'; } Super.prototype.func=function(){alert('Super');}; function Sub() { this.x='1'; } Sub.prototype =Super.prototype; Sub.prototype.test=function(){alert('Sub');} var sub=new Sub(); sub.func();//'Super' sub.test();//'Sub' sub.attr;//undefined sub.x;//1
上述代码中,将Super.prototype直接赋给Sub.prototpye,发现对Sub类型实例对象sub,访问attr属性时,无法获取
这就是因为attr属性定义在Super构造函数中,而不是定义在Super的原型对象上,此时Super.prototype中并没有attr属性
假设将Super所有属性都定义在原型对象上,如下所示,这时sub实例对象是可以访问到父类的attr属性的
但这时,Super类所有实例对象,都共享了attr属性,一个实例对attr属性的更改,会引起其他实例对象attr属性值的变更,这是应该避免的
function Super() { } Super.prototype.attr='a'; Super.prototype.func=function(){alert('Super');}; function Sub() { this.x='1'; } Sub.prototype =Super.prototype; Sub.prototype.test=function(){alert('Sub');} var sub=new Sub(); sub.attr;//a sub.x;//1
再换一种写法看看
function Super() { this.attr='a'; } Super.prototype.func=function(){alert('Super');}; function Sub() { this.x='1'; } Sub.prototype =Super(); Sub.prototype.test=function(){alert('Sub');} var sub=new Sub(); sub.func(); sub.test(); sub.attr; sub.x;
实际上,上面代码会直接报错,提示test属性没有被定义
原因是Sub.prototype=Super();这句代码,实际上是将Sub.prototype指向了undefined,因为Super()语句,是在以函数行驶调用Super()方法,这一调用,是没有返回值的
经过这一调用,Sub.prototype此时已经为undefined,再为他定义test属性,自然就报错了