一原型链继承
var B = function(){
this.result = false;
this.arr = [1];
}
var C = function(){};
C.prototype = new B();
var c1 = new C();
var c2 = new C();
c1.arr.push(2);
alert (c1.arr); // 1,2 alert(c2.arr); //1,2
- 上面的过程实现了原型链继承,核心是让父类实例充当子类原型对象,这样子类函数就原原本本的继承了父类函数的内容。
- 缺点
(1)子类函数的所有实例都会共享子类函数继承到的内容,一但通过子类函数的实例去改变继承到的内容,那么其他的实例继承到的内容也会跟着改变。
(2)创建子类实例时不能向超类构造函数(父类函数)传递参数。 - 分析原因
我们知道实例继承构造函数的内容由两部分组成,第一是构造函数自身所带的即写在函数里的内容;第二是通过原型链得到的内容。
由于原型链继承采用的是让子类函数的原型对象等于父类函数的实例。所以子类实例继承子类函数的内容都是在子类函数原型对象上的内容。由于原型链的存在导致原型对象上的内容会被原型链上当前原型对象往后的对象所继承,于是只要你改变了原型对象的值,那么往后继承自该原型对象的实例的内容都会一起改变。
二借用构造函数继承
var B = function(val){
this.val = val;
this.arr = [1];
this.fun = function(){ ... };
}
var C = function(val){
B.call(this,val)
}
var c1 = new C(1); var c2 = new C(2); c1.arr.push(2);
alert(c1.arr);//1,2 alert(c2.arr);//1 alert(c1.fun===c2.fun);//false
- 核心思想是在子类构造函数的内部调用超类型构造函数(父类构造函数),相当于把父类的实例属性复制一份给子类实例。
- 优点
(1)解决了子类实例共享引用的父类属性问题,每个实例都有自己的属性副本。
(2)创建子类实例时可以向父类构造函数传参 - 缺点
无法实现函数复用,每个子类实例都有一个新的fun函数,当创建大量子类实例时会影响性能
三组合继承
function person(name){
this.name = name;
this.arr = [1];
}
person.prototype.sayName = function(){
alert(this.name);
}
function man(name,age){
//继承属性
person.call(this,name);
this.age = age;
}
//继承方法
man.prototype = new person();
man.prototype.constructor = man;
man.prototype.sayAge = function(){
alert(this.age);
}
- 这种继承是将原型链和借用构造函数的技术组合在一起,具体方法是用原型链实现对原型属性和方法的继承(公用的,方便函数复用),通过借用构造函数来实现对实例属性的继承(私有的,每个实例自己的属性)。
- 分析
person构造函数定义了两个属性name和arr。person的原型中定义了一个方法sayName()。man构造函数通过call方法调用person函数时传入参数name,并且定义了自己的属性age。然后将person函数的实例赋给man函数的原型,接着在新原型上定义了方法sayAge()。这样就可以让man函数的实例分别拥有自己的属性age包括name和arr,又可以使用相同的方法sayName()和sayAge()。
四原型式继承
function object(o){
function F(){}
F.prototype = o;
return new F();
}
- 该继承的思想是借助原型可以基于已有的对象创建新对象,省去创建自定义类型。具体做法是在object()函数内部先创建一个临时性构造函数,然后将传入的对象作为这个构造函数的原型,最后返回这个临时类型的一个新实例。其实相当于object()函数对传入其中的对象执行了一次浅复制。
- create()方法
ES5中新增create()方法规范原型式继承,该方法接收两个参数。一个用作新对象原型的对象和一个为新对象定义额外属性的对象。只传一个参数时该方法与上面的object()方法相同。第二个参数定义的属性会覆盖原型对象上的同名属性
var person = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"]
};
var anotherPerson = Object.create(person, {
name: {
value: "Greg"
}
});
alert(anotherPerson.name); //"Greg"
五寄生式继承
function createAnother(original){
var clone = object(original); //通过调用函数创建一个新对象
clone.sayHi = function(){ //以某种方式来增强这个对象
alert("hi");
};
return clone; //返回这个对象
}
- 思想是创建一个仅用于封装继承过程的函数,在该函数内部以某种方式来增强对象,最后返回对象。实现过程是createAnother()函数接收一个参数,也就是将要作为新对象基础的对象。然后,把这个对象(original)传递给 object()函数,将返回的结果赋值给 clone。再为 clone 对象添加一个新方法 sayHi(),最后返回 clone 对象。
- 缺点
使用寄生式继承来为对象添加函数,会由于不能做到函数复用而降低效率;这一
点与构造函数模式类似。
六寄生组合式继承
function inheritPrototype(subType, superType){
var prototype = object(superType.prototype); //创建对象
prototype.constructor = subType; //增强对象
subType.prototype = prototype; //指定对象
}
*接收两个参数,子类构造函数和超类构造函数*
*第一步是利用原型式继承创建一个超类原型的副本*
*第二步让创建副本的构造函数等于子类构造函数,因为在原型式继承中重写原型失去了默认的
constructor属性(没有这一步的话,prototype.constructor指向Object()函数)*
*第三步是将副本赋给子类型的的原型*
下面为使用寄生组合式实现继承的完整过程
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);
};
- 组合继承结合了原型链继承和借用构造函数的优点,唯一的不足是调用了两次超类型构造函数。寄生组合式继承解决了该问题,在只调用一次超类函数的情况下既实现了函数的复用又能让每个实例有自己的属性。