1. 原型链
原型链是实现继承的主要方法,它是利用原型,让一个引用类型继承另一个应用类型的属性和方法。
- 实现原型链的基本模式:
//定义父类构造函数
function Person(){
this.PersonImprint = "from_Person";
}
Person.prototype.PersonSay = function(){
console.log(this.PersonImprint);
}
//定义子类构造函数
function Son(){
this.SonImprint = "from_Son";
}
//初始化子类构造函数原型为父类实例----实现继承父类属性和方法。
Son.prototype = new Person();
var sonExample = new Son();
console.log(sonExample.PersonImprint) //from_Person
sonExample.PersonSay()) //from_Person
console.log(sonExample.SonImprint) //from_Son
上面将
Son
的原型指向了Person
的实例,这样Son
的新原型不仅具有Person
的实例所拥有的全部属性和方法,而且其原型内部还有一个指针,指向了Person
的原型。
最终得出:sonExample
指向Son
的原型,而Son
的原型又指向Person
的原型。
//实例检测
sonExample instanceof Son //true
sonExample instanceof Person //true
sonExample instanceof Object //true
//原型检测
sonExample.prototype.isPrototypeOf(Son) //true
sonExample.prototype.isPrototypeOf(Person) //true
sonExample.prototype.isPrototypeOf(Object) //true
由于原型链的关系,可以说
sonExample
是Son
、Person
、Object
中任何一个类型的实例。
-
原型链注意事项
- 子类有时候需求覆盖超类型中的某个方法,或者添加超类型中的某个不存在的属性或方法,这种操作代码,需要放在继承(
替换原型
)之后。- 实现继承时,不能以对象字面量创建原型,这样会重写原型。
-
原型链的问题
- 依据原型的“共享”这一点看出的,虽然说是通过原型来实现继承,但是其原型其实是另一个类型的实例。这样原来的实例属性也就成为了现在原型的属性了。
- 在创建子类的实例时,不能向超类的构造函数中传递参数。
2. 借用构造函数
这种方法的基本思想相当简单,即在子类型构造函数的内部通过call()和apply()
调用超类型构造函数。
function SuperType(){
this.colors = ["red","blue","green"];
}
function SubType(){
SuperType.call(this)
}
var instance1 = new SubType();
instance1.colors.push("black");
//["red","blue","green","black"];
var instance2 = new SubType();
//["red","blue","green"];
上面子类的构造方法
SubType
中“借调”了超类SuperType
的构造函数,在创建新的实例过程中,运行了超类构造函数,这样一来子类就拥有了超类函数中定义的所有属性了。
-
传递参数
function SuperType(name){
this.name = name;
}
function SubType(){
SuperType.call(this,"余嘉")
}
var instance1 = new SubType()
-
借用构造函数的弊端
- 如果是仅仅借用构造函数(超类),那么构造函数(超类)的存在意义就是问题所在,因为方法都在超类中定义,因此构造函数的复用就无从谈起了。
- 超类中给原型定义的方法,对于子类型而言是看不见的。
3. 组合继承(原型链+借用构造函数)
将原型链
和借用构造函数
组合使用,利用两者的长处。
function superType(name){
this.name = name;
this.colors = ["red","blue","green"];
}
superType.prototype.sayName = function(){
console.log(this.name)
}
funtionc subType(name,age){
superType.call(this,name);
this.age = age;
}
subType.prototype = new superType();
subType.prototype.constructor = subType;
subType.prototype.sayAge = function(){
console.log(this.age)
};
var instance1 = new subType("余嘉",27);
//subType {name: "余嘉", colors: Array(3), age: 27};
instance1.sayAge() // 27
instance1.sayName() // "余嘉"
组合继承避免了原型链和借用构造模式的缺陷,融合了他们的优点,是项目中最常用的继承模式。
4. 原型式继承(Object.create( obj , newKeyobj)
)
借助原型可以基于已有的对象(实例)创建新对象,同事还不必因此创建自定义类型。
function create(0){
function F(){}
F.prototype = o;
return new F();
}
在
create()
函数内部,先创建一个临时性的构造函数,然后将传入的对象O
作为这个构造函数的原型(浅复制
),然后返回临时构造的实例。
es5新增的Object.create(
obj , newKeyobj)
规范这个原型式继承,它接受两个参数
- (必须的)obj 作为新对象的原型对象
- (可选的)为新对象定义额外属性对象,和
Object.difineProperties()
方法的参数一样。
var person = {
name : "余嘉",
friends:["cat","dog"]
}
var otherPerson1 = Object.create(person);
otherPerson1.name = "小微";
otherPerson1.friends.push("萝卜");
var otherPerson2 = Object.create(person);
otherPerson1.name = "达令";
otherPerson1.friends.push("白菜");
console.log(person.friends);
//["cat","dog","萝卜","白菜"]
var person = {
name : "余嘉",
friends:["cat","dog"]
}
var otherPerson1 = Object.create(person,{
name:{
value:"小微"
}
});
- “共享”的利与弊,永远是原型模式的烙印。
5. 寄生式继承
寄生式继承的思路与寄生构造函数和工厂模式类似,创建一个经用于封装继承过程的函数。
function createAnother(original){
var clone = Object(original);
clone.sayHi = function(){
console.log("Hi")
};
return clone
}
调用
var person = {
name:"余嘉",
friends:["Shelby", "Court", "Van"]
}
var anotherPerson = createAnother(person);
anotherPerson.sayHi();
//Hi