九、高级面向对象技术之继承
1.工厂函数模式创建对象
例如:
function factory(name,age){
var obj = new Object();
obj.name = name;
obj.age = age;
obj.sayName = function(){
};
return obj;
}
var obj1 = factory("terry",12);
var obj2 = factory("larry",12);
此模式存在的问题有:对象类型无法细分;其中的函数会重复创建,浪费内存空间且破坏封装性。
2.构造函数模式创建对象
通过Object、Array、Date、Number、String、Boolean、RegExp等系统构造函数,或通过自定义构造函数,如function Student(){},创建对象。此模式解决了对象类型无法细分的问题,但仍未解决函数重复创建,破坏封装性的问题。
3.构造函数模式与原型相结合模式
此模式可将对象属性保存在相应实例中,而方法保存在实例的构造函数原型中,如:
function Person(name,age){
this.name = name;
this.age = age;
}
Person.prototype.sayName = function(){
console.log(“Hi, my name is” + this.name);
}
var p1 = new Person("terry",12);
var p2 = new Person("larry",13);
可用obj instanceof Function判断obj是否是Function的实例。
4.原型链继承
每个构造函数都有一个原型对象,原型对象中都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。当原型对象等于另外一个类型的实例即继承。调用某个方法或者属性的步骤。其核心在于:子构造函数的原型指向(是)父构造函数的实例,如Dog的原型Dog.prototype是Object的实例。
再例如:
//定义父类类型
function Animal(){
this.name = "animal"
}
Animal.prototype.sayName = function(){
console.log(this.name);
}
//定义子类类型
function Dog(){
this.color = "灰色"
}
//通过将子对象的原型对象指向父对象的一个实例来完成继承
Dog.prototype = new Animal();
//区分父类实例的类型
Dog.prototype.constructor = Dog;
//子对象的方法其实是定义在父类对象的实例上
Dog.prototype.sayColor = function(){
console.log(this.color);
}
var dog = new Dog();
console.log(dog); //Dog { color: '灰色' }
dog.sayColor(); //灰色
dog.sayName(); //animal
在子类构造函数内部调用超类构造函数可以简化其初始化过程,原理是使用原型链实现对原型属性和方法的继承,而通过借用构造函数实现对实例属性的继承,例如:
function Animal(name,age){
this.name = name;
this.age = age;
}
Animal.prototype.sayName = function(){
console.log("my name is",this.name);
}
function Person(name,age,gender){
Animal.call(this,name,age);
this.gender = gender;
}
Person.prototype = new Animal();
Person.prototype.constructor = Person; //替换原型
Person.prototype.sayGender = function(){
console.log("my gender is",this.gender);
}
var p = new Person("terry",12,"male");
console.log(p); //Person { name: 'terry', age: 12, gender: 'male' }
p.sayName(); //my name is terry
p.sayGender(); //my gender is male
子类型覆盖超类型中的某个方法,或者是需要添加超类中不存在的方法,都需要将给原型添加方法的代码放在继承之后(即替换原型的语句之后),且创建子类对象的操作也应在替换原型之后进行,否则会出现“同母异父”的情况,例如:
function Dog(name,age){
this.name = name;
this.age = age;
}
Dog.prototype.sayName = function(){
console.log("hi, my name is",this.name);
}
var d1 = new Dog("一休",2);
console.log(d1);
Dog.prototype = {
constructor:Dog,
sayName:function(){console.log(this.name)},
sayAge:function(){console.log(this.age);}
}
var d2 = new Dog("八哥",2);
console.log(d2);
console.log(d1.constructor === d2.constructor); //true
console.log(d1.__proto__ === d2.__proto__); //false