继承是面向对象的重要特性,子类继承了父类,子类就有了父类的属性和方法。在java中使用extends关键字后加父类名就可以实现继承。但在javascript中没有这种关键字。那么javascript如何实现继承呢? 在这里可以使用原型来实现继承,原型对象是在每个方法在创建之时就根据一定规则生成的。如果使一个方法原型对象的指针指向一个实例,此时的原型对象将包含指向另一个原型对象的指针,相应的,另一个原型对象将指向另一个构造函数。如果另一个原型对象又指向另一个实例,那么上述关系依然成立,这样层层递进,形成了一个原型链。
function Person(){
}
Person.prototype.name = "zhouqi";
Person.prototype.age = 25;
Person.prototype.job = "computer scientist";
Person.prototype.sayHi = function(){
alert("Hi");
}
function Man(){
}
Man.prototype = new Person();
var man1 = new Man();
alert(man1 instanceof Person);
alert(man1 instanceof Object);
man1.sayHi();
如上面例子所示,Man的原型对象指向一个Person实例,那么原型对象中就有一个指向Person原型的指针_proto_。
但是用原型链来实现继承并不是最好的方法,在使用原型进行对象创建时有同样的问题,在使用原型创建对象时,不同的实例将共享原型中的属性,如果属性是一个引用类型会产生改变一个实例值,其他实例的值也跟着改变的问题。
<script type="text/javascript">
alert("fuck");
function Person(){
}
Person.prototype.name = "zhouqi";
Person.prototype.age = 25;
Person.prototype.friends = new Array("zhangyan", "wangjianfei","peixin");
function man(){
}
man.prototype = new Person();
man.prototype.sayHi = function(){
alert("hello");
}
var man1 = new man();
var man2 = new man();
man1.friends.push("guxin");
alert(man1.friends);//zhangyan,wangjianfei,peixin,guxin
alert(man2.friends);//zhangyan,wangjianfei,peixin,guxin
</script>
如上面例子所示,为man1添加一个朋友,结果也为man2添加了一个朋友,这样会产生问题。为了解决这个问题,可以使用借用构造函数的方式,这种方式十分简单,可以看下面的例子。
<script type="text/javascript">
function Person(){
this.friends = new Array("zhouqi","guxin","wangjainfei");
}
function Man(){
Person.call(this);
}
var man1 = new Man();
var man2 = new Man();
man1.friends.push("xiaoqiaofeng");
alert(man1.friends);//zhouqi,guxin,wangjainfei,xiaoqiaofeng
alert(man2.friends);//zhouqi,guxin,wangjainfei
</script>
在子类对象中调用父类对象,会将父类对象的作用域添加到子类对象中,子类对象就可以调用父类对象的属性和方法,这种方式简单明了。但借用构造函数存在这样一个问题,所有的方法都在构造函数中定义,那么函数的复用会成为一个问题,为了解决这个问题,可以使用组合继承方式,即将原型链和构造函数方式结合起来。如下面例子所示
<script type="text/javascript">
function Person(){
this.friends = ["guxin","zhouqi"]
}
Person.prototype.sayHi = function(){
alert("hello");
}
function Man(){
Person.call(this);
}
Man.prototype = new Person();
var man1 = new Man();
man1.sayHi();
man1.friends.push("jianfei");
var man2 = new Man();
alert(man1.friends);//guxin,zhouqi,jianfei
alert(man2.friends);//guxin,zhouqi
</script>
这种继承方式被称为组合继承,组合继承结合了原型链函数可复用的优点,又避免了原型链数据共享的问题,将需要共享的数据放在原型链中,将不需共享的内容或因对象而异的内容放在构造函数中。
除了本文介绍的几种基础的构造函数方式,还有原型式继承,寄生式继承,寄生组合式继承等继承方式,可以参考《javascript 高级语言程序设计》这本书。通过学习javascript面向对象编程这个章节,个人认为javascript并不是严格的面向对象编程语言。不像java,C++,python。但通过javascript的一些语法特点可以实现面向对象编程,但这比java要复杂一些。这一章的关键点是构造函数和原型、原型链的概念。构造函数内部的属性和方法每创建一个对象都要创建一次,而原型中的属性和方法是所有实例共享的。无论是创建对象还是实现继承单独使用构造函数和原型都会产生问题,单独使用构造函数模式会产生函数复用问题,单独使用原型则会产生共享问题。所以要将两者结合起来使用。