1、原型链继承
- 原理:通过原型链将子类的原型指向父类的实例
function FarType(name) {
this.name = name
this.color = ['red', 'blue', 'yellow']
}
FarType.prototype.showName = function() {
console.log(this.name)
}
function SonType(age) {
this.age = age
}
SonType.prototype = new FarType();
SonType.prototype.showAge = function() {
console.log(this.age)
}
- 测试
let son_1 = new SonType(12)
son_1.color.push('black')
console.log(son_1.color) //["red", "blue", "yellow", "black"]
son_1.showAge() //12
son_1.showSex() //male
son_1.showName() //undefined
let son_2 = new SonType(22)
console.log(son_2.color) //["red", "blue", "yellow", "black"]
son_2.showAge() //22
son_2.showSex() //male
son_2.showName() //undefined
-
缺点
- 父类包含引用类型值的原型,它会被所有实例共享,因为子类的prototype指向了父类的实例,父类实例中的color指针一直保持不变
- 创造子类实例时,不能向父类的构造函数中传递参数(参考父类中的name并没有被赋值)
-
优点
- 子类可以通过原型链共享父类原型中的方法
-
注意事项
- 子类原型添加方法的代码一定要放在子类替换原型的语句之后,否则子类原型的方法还是添加到了默认的子类原型中。
2、借用构造函数继承
- 原理:在子类型构造函数的内部调用父类构造函数,使用apply()和call()方法将this指向子类
function FarType(name) {
this.name = name
this.sex = 'male'
this.color = ['red', 'blue', 'yellow']
}
FarType.prototype.showName = function() {
console.log(this.name)
}
FarType.prototype.showSex = function() {
console.log(this.sex)
}
function SonType(age, name) {
FarType.call(this, name)
this.age = age
}
SonType.prototype.showAge = function() {
console.log(this.age)
}
- 测试
let son_1 = new SonType(12, 'son_1')
son_1.color.push('black')
console.log(son_1.name) //son_1
console.log(son_1.color) //["red", "blue", "yellow", "black"]
son_1.showAge() //12
// son_1.showSex() //Uncaught TypeError: son_1.showSex is not a function
let son_2 = new SonType(22, 'son_2')
console.log(son_2.name) //son_2
console.log(son_2.color) //["red", "blue", "yellow"]
son_2.showAge() //22
son_2.showSex() //Uncaught TypeError: son_2.showSex is not a function
-
缺点
- 子类的原型无法通过原型链到父类原型,即父类原型中的方法无法使用(如果想使用方法,只能定义在父类中)
-
优点
- 字实例都会有自己的数据的副本,引用类型在每个子类型实例中都已单独的一个副本,不会相会影响
- 创造子类实例时,可以向父类的构造函数中传递参数(参考子类实例中的那么属性)
3、组合继承(原型链继承和借用构造函数继承结合)
- 原理:将原型链继承和借用构造函数继承组合到一起。使用原型链继承实现对原型属性和方法的继承,用借用构造函数继承实现对父实例属性的继承。这样既通过在原型上定义方法实现了函数复用,又能保证每个实例都有自己的属性
function FarType(name) {
this.name = name
this.sex = 'male'
this.color = ['red', 'blue', 'yellow']
}
FarType.prototype.showName = function() {
console.log(this.name)
}
FarType.prototype.showSex = function() {
console.log(this.sex)
}
function SonType(age, name) {
FarType.call(this, name)
this.age = age
}
SonType.prototype = new FarType();
SonType.prototype.showAge = function() {
console.log(this.age)
}
- 测试
let son_1 = new SonType(12, 'son_1')
son_1.color.push('black')
console.log(son_1.name) //son_1
console.log(son_1.color) //["red", "blue", "yellow", "black"]
son_1.showAge() //12
son_1.showSex() //male
let son_2 = new SonType(22, 'son_2')
console.log(son_2.name) //son_2
console.log(son_2.color) //["red", "blue", "yellow"]
son_2.showAge() //22
son_2.showSex() //male
-
缺点
1、会调用两次超类型构造函数,一次是在创建子类型原型的时候,一次是在子类型构造函数的内部,占用内存。
2、由于调用了两次父构造函数在子类实例的原型对象上会有冗余的属性存在 -
优点
1.既可以继承父类原型中的方法和属性,也可以为父构造函数传递参数,保证每个子实例都有自己的属性(副本)
4、原型继承
- 原理:不用严格意义上的构造函数,借助原型可以根据已有的对象创建新对象,不必因此创建自定义类型
function obj(o){
function F(){}
F.prototype = o;
return new F();
}
- 测试
let animals = {
dog : 12,
cat : 20,
arr : [1,2,3]
}
let res_1 = obj(animals)
res_1.cat = 50
res_1.arr.push(4)
console.log(res_1)
let res_2 = obj(animals)
console.log(res_2)
- 注意:原型继承实际上是创建了animals 的两个副本,因为arr的指针没有发生变化,所以会出现上图arr在两个对象中相同的情形
-
规范: 可以参考es5中
Object.create(obj, des)
方法。第一个参数obj是原型对象,第二个参数des是生成对象的属性描述符descriptors
5、寄生式继承
- 原理:寄生式继承是与原型式继承紧密相关的一种思路,它创造一个仅用于封装继承过程的函数,在函数内部以某种方式增强对象,最后再返回对象。其实就是原型继承封装了一层而已
function parasitic(original){
let clone = obj(original)
clone.sayHello = function(){
console.log("hello, World");
};
return clone
}
console.log(parasitic({a:1}))
6、寄生组合式继承(终极目标)
- 原理:通过借用构造函数来继承属性,通过原型链的混成形式来继承方法,不必为了指定子类型的原型而调用超类型的构造函数,只需要超类型的一个副本。本质上,就是使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型(这个理解了,就ok)
function inheritPrototype(sonType,farType){
let prototype = obj(farType);
prototype.constructor = sonType;
sonType.prototype = prototype ;
}
- 测试
function FarType(name){
this.name = name;
this.color = ["red","green","blue"];
}
FarType.prototype.sayName = function(){
console.log(this.name);
};
function SonType(name,age){
FarType.call(this,name);
this.age = age;
}
inheritPrototype(SonType,FarType.prototype);
SonType.prototype.sayAge = function(){
console.log(this.age);
};
let son_1 = new SonType('son_1',12)
son_1.color.push('black')
console.log(son_1.color) //["red", "green", "blue", "black"]
son_1.sayAge() //12
son_1.sayName() //son_1
let son_2 = new SonType('son_2',20)
console.log(son_2.color) //["red", "green", "blue"]
son_2.sayAge() //20
son_2.sayName() //son_2
- 优点
- 只调用了一次Fartype构造函数,因此避免在SonType.prototype上创建不必要的,多余的属性,与此同时,原型链还能保持不变,还能正常使用instanceof 和isPrototypeOf(),因此,寄生组合式继承被认为是引用类型最理想的继承范式。
结尾
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。2018完了。。。