1.原型链继承
这是最简单的一种方式,使用
Son.prototype = Parent();
这样就可以让Son的原型指向Parent ,
这是最简单的一种继承方式
它可以使用父级的方法和属性
但是它有两个缺点 :
-
第一个是,当实现继承后,它是以引用值当做原型的,这样会导致所有的子类型的原型指向同一个地方,当改变了其中一个子类型继承的属性时,因为子类型公用一个引用值,所有的引用属性都会被改变。
-
第二个是,在创建子类型的实例时,没有建立俩个类型的关系,无法向父级构造函数传递参数.
2.构造函数继承
原型中有引用值的问题,我们可以使用借用构造函数,来解决这个问题
function Parent() {
this.colors = ["red", "blue", "green"];
this.saySome = function (){
console.log('hello');
}
}
function Son() {
//继承Parent
Parent.call(this);
}
var instance1 = new Son();
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
var instance2 = new Son();
alert(instance2.colors); //"red,blue,green"
instance1.saySome(); //"ERROR"
这样的话,每次生成一个实例,都会保存一个parent的副本,就能保证每个实例继承属性的独立性
而且还解决了传参的问题,需要向父级对象传参的时候,在call的第一个参数之后按顺序写入要传递给父级的参数即可.
但是它还是有一定的问题,在js中超类型定义的方法是子类型不可见的,也就是说这样继承的子类型是无法使用超类型的方法的.
3. 组合继承
function Parent(name) {
this.name = name;
this.colors = ["red", "blue", "green"];
}
Parent.prototype.sayName = function() {
alert(this.name);
}
function Son(name, age) {
// 继承属性
Parent.call(this, name);
this.age = age;
}
// 继承方法
Son.prototype = new Parent();
Son.prototype.constructor = Son;
Son.prototype.sayAge = function() {
alert(this.age);
};
var instance1 = new Son("Nicholas", 29);
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
instance1.sayName(); //"Nicholas";
instance1.sayAge(); //29
var instance2 = new Son("Greg", 27);
alert(instance2.colors); //"red,blue,green"
instance2.sayName(); //"Greg";
instance2.sayAge(); //27
-
将Son的原型指定为Parent的一个实例,大致步骤和原型链继承类似,只是多了在Son中借调Parent的过程。
-
实例属性定义在构造函数中,而方法则定义在构造函数的新原型中,同时将新原型的constructor指向构造函数。
-
可以通过instanceof和isPrototypeOf()来识别基于组合继承创建的对象。
-
避免了原型链和借用构造函数的缺陷,融合了它们的优点,成为JS中最常用的继承模式。
他解决子类型继承属性引用的原理是借用了构造函数,以覆盖的方式,解决了在原型链继承中原型的引用类型属性共享在所有实例中的问题。
4.原型式继承
他的方法就是临时创建一个构造函数,借助已有的对象(obj)作为临时构造函数的原型,然后实例化对象,并返回。
function object(obj){
function F(){}
F.prototype = obj;
return new F();
}
var person = {
name: "father",
friends: ["Shelby", "Court", "Anna"]
};
var anotherPerson = object(person);
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");
var yetAnotherPerson = object(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.friends.push("Barbie");
alert(person.friends); //"Shelby,Court,Van,Rob,Barbie"
- 这种继承的继承属性也是引用值.
5.寄生式继承
function createAnother(obj) {
var clone = object(obj);
clone.sayHello = function() {
alert("hello");
};
return clone;
}
它是在原型式继承得到对象的基础上,在内部再以某种方式来增强对象,然后返回。
比如上面的例子就是在实例化对象之后,在新函数中还能给他添加一个sayHello方法.
寄生组合式继承
组合继承是JS中最常用的继承模式,它的不足在于无论什么情况下都会调用两次超类型的构造函数,并且创建的每个实例中都要屏蔽超类型对象的所有实例属性。
寄生组合式继承就能解决问题,下面看看例子.
function object(obj) {
function F(){}
F.prototype = obj;
return new F();
}
function inheritPrototype(Parent, Son) {
var prototype = object(Parent.prototype);
prototype.constructor = Son;
Son.prototype = prototype;
}
function Parent(name) {
this.name = name;
this.colors = ["red", "blue", "green"];
}
Parent.prototype.sayName = function() {
alert(this.name);
};
function Son(name, age) {
Parent.call(this, name);
this.age = age;
}
inheritPrototype(Parent, Son); // 这里相当于Son.prototype = new Parent();可以少调用一次Parent();
Son.prototype.sayAge = function() {
alert(this.age);
};
这种方法简化的原理是我们不必为了指定子类型的原型而调用超类型的构造函数,我们需要的只不过是超类型原型的一个副本。
所以我们使用inheritProrToType()函数来解决这些问问题
我们在inheritPrototype函数中用到原型式继承,将超类型的原型指定为一个临时的空构造函数的原型,并返回构造函数的实例。
此时由于构造函数内部为空,所以返回的实例也不会自带实例属性,这很重要!因为后面用它作为Son的原型时,就不会产生无用的原型属性了,借调构造函数也就不用进行所谓的“重写”了。
然后为这个对象重新指定constructor为Son,并将其赋值给Son的原型。这样,就达到了将超类型构造函数的实例作为子类型原型的目的,同时没有一些从Parent继承过来的无用原型属性。