面向对象设计——继承
序:回顾构造函数、原型和实例之间的关系
每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。
1.原型链
function Super(){ this.property = true; } Super.prototype.getSuperValue = function(){ return this.property; }; function Sub(){ this.subproperty = false; } Sub.prototype = new Super(); //给原型添加方法的代码一定要放在替换原型的语句之后 //添加新方法 Sub.prototype.getSubValue = function(){ return this.subproperty; }; //重写超类型的方法 Sub.prototype.getSuperValue = function(){ return false; } var instance = new Sub(); alert(instance.getSuperValue()); //false
缺点:
(1)引用类型的原型实例会被所有实例共享。
(2)子类不能向超类传递参数。
2.借用构造函数
function Super(name){ this.name = name; } function Sub(){ //继承了Super,同时还传递了参数 Super.call(this, "Nicholas"); //实例属性 this.age = 29; } var instance = new Sub(); alert(instance.name); //"Nicholas" alert(instance.age); //29
缺点:无法避免构造函数存在的问题——方法都在构造中定义,因此函数复用就无从谈起了。
3.组合继承(常用)
将原型链和借用构造函数组合到一块
function Super(name){ this.name = name; this.colors = ["red","blue","green"]; } Super.propotype.sayName = function(){ alert(this.name); }; function Sub(name,age){ //继承属性 Super.call(this, name); //第二次调用Super this.age = age; } //继承方法 Sub.prototype = new Super(); //第一次调用Super Sub.prototype.constructor = Sub; Sub.propotype.sayAge = function(){ alert(this.age); }; var instance1 = new Sub("Nicholas",29); instance1.colors.push("black");
缺点:会两次调用父类型的构造函数。
4.原型式继承
必须有一个对象作为另一个对象的基础。
ECM5通过新增Object.create()方法规范了原型式继承。这个方法接收两个参数:一个用作新对象原型的对象和(可选的)一个为新对象定义额外属性的对象。
function object(o){ function F(){} F.prototype = o; return new F(); } var person = { name: "Nicholas", friends: ["Shelby","Court","Van"] }; var anotherPerson = object(person); anotherPerson.friends.push("Rob"); alert(person.friends); //"Shelby,Court,Van,Rob"
优势:不想兴师动众的创建构造函数,只想一个对象与另一个对象保持相似的情况下,原型式可以完全胜任。
缺点:属性共享。
5.寄生式继承
function createAnother(original){ var clone = object(original); //object()函数不是必须的,任何返回新对象的函数都可以 clone.sayHi = function(){ alert("hi"); }; return clone; } var person = { name: "Nicholas", friends:["Shelby","Court","Van"] }; var anotherPerson = createAnother(person); anotherPerson.sayHi(); //"hi"
6.寄生组合式继承(最理想的继承范式)
原理:不必为了指定子类型的原型而调用超类型的构造函数,我们所需要的无非是一个超类型原型的副本。
function inheritPrototype(subType, superType){ var prototype = object(superType.prototype); prototype.constructor = subType; subType.prototype = prototype; } function SuperType(name) { this.name = name; this.colors = ["red","blue","green"]; } SuperType.prototype.sayName = function(){ alert(this.name); } function SubType(name,age){ SuperType.call(this,name); this.age = age; } inheritPrototype(SubType, SuperType); SubType.prototype.sayAge = function(){ alert(this.age); }