JavaScript面向对象和继承机制
(6)组合使用构造函数模式和原型模式:
function School(name, teacher) { this.name = name; this.teacher = teacher; this.students = ['任中原', '秦海川'] } School.prototype = { construcor: School, //因为是字面量类型定义原型,相当于重写了原型,要重新设置构造函数的指向 sayName: function() { alert(this.name); } } var school1 = new School('珠峰培训', '耿老师'); var school2 = new School('珠峰培训', '林老师'); school1.students.push('邓伟'); alert(school1.students); // '任中原','秦海川','邓伟' alert(school2.students); // '任中原','秦海川' alert(school1.students == school2.students); //输出结果false alert(school1.sayName == school2.sayName); //输出结果true
注意:
a.实例属性都是在构造函数中定义的,而所有实例共享的属性和方法都是在原型中定义的。
b.这种构造函数与原型混成的模式,是目前在ECMAScript中使用的最广泛、认同度最高的一种创建自定义的方法。可以说,这是用来定义引用类型的一种默认模式。
继承机制
继承是面向对象语言的三大特性之一(继承、封装、多态),许多面向对象语言都支持两种继承方式:接口集成和实现集成。接口继承只继承方法签名,而实现集成则继承实际的方法。由于函数没有签名,在ECMAScript中无法实现接口继承。ECMAScript只支持实现继承,而且其实现继承主要是依靠原型链来实现继承。
原型链
原型链的基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。
代码如下:
function SuperType() { this.name = '珠峰培训'; this.teachers = ['耿老师', '林老师']; } function SubType() {} //继承了SuperType SubType.prototype = new SuperType(); //共享了实例 var instance1 = new SubType(); instance1.teachers.push('刘老师'); alert(instance1.teachers); //'耿老师','林老师','刘老师' var instance2 = new SubType(); alert(instance2.teachers); //'耿老师','林老师','刘老师'
原型链的问题:
(1)共享了引用类型属性和方法。
(2)创建子类型的实例时,不能向超类型的构造函数中传递参数。
借用构造函数
这种技术的基本思想是在子类型构造函数的内部调用超类型构造函数。
function SuperType(name) { this.name = name; this.teachers = ['耿老师', '林老师']; this.sayName = function() { //每实例化一次创建一个function对象 alert(this.name); } } function SubType() { //继承了SuperType SuperType.call(this, name); } var instance1 = new SubType('珠峰培训'); instance1.teachers.push('刘老师'); alert(instance1.teachers); //'耿老师','林老师','刘老师' var instance2 = new SubType(); alert(instance2.teachers); //'耿老师','林老师'
借用构造函数的问题:
(1)如果仅仅是借用构造函数,那么也将无法避免构造函数模式存在的问题——方法都在构造函数中定义,因此函数复用就无从谈起了。
this.sayName = function() { //每实例化一次创建一个function对象 alert(this.name); }
(2)在超类型的原型中定义的方法,对子类型而言也是不可见的,结果所有类型都只能使用构造函数模式。
组合继承
将原型链和借用构造函数的技术组合到一块,从而发挥二者之长的一种继承模式。也是JavaScript中最常用的继承模式。
function SuperType(name) { this.name = name; this.teachers = ['耿老师', '林老师']; } SuperType.prototype.sayName = function() { alert(this.name); } function SubType(name, address) { SuperType.call(this, name); this.address = address; } //继承 SubType.prototype = new SuperType(); SubType.prototype.constructor = SubType; SubType.prototype.sayAddress = function() { alert(this.address); }; var instance1 = new SubType('珠峰培训', '西苑'); instance1.teachers.push('刘老师'); alert(instance1.teachers); //'耿老师','林老师','刘老师' instance1.sayName(); //'珠峰培训' instance1.sayAddress(); //'西苑' var instance2 = new SubType('zhufengpeixun', '西苑'); alert(instance2.teachers); //'耿老师','林老师' instance2.sayName(); //'zhufengpeixun' instance2.sayAddress(); //'西苑'
因为SubType原型是一个SuperType实例,此时SubType.prototype.constructor指向SuperType构造函数,所以第263行需要重新给SubType.prototype.constructor定义指针指向SubType的构造函数,调试代码如下: