几年前将js
继承整合过,但是稿纸不知道我放在哪里了,所以打算在整合一遍,放在这里就不会搞丢了,方便我复习巩固。
继承有哪些:
- 原型链继承
- 借用构造函数继承
- 组合继承
- 原型式继承
- 寄生式继承
- 寄生组合继承
原型链继承
基本思想:将父类的实例作为子类的原型
// 父类
function SuperFun(){
this.name = 'Super';
this.colors=['red','blue']
}
SuperFun.prototype.sayName = function (params) {
console.log(this.name)
}
// 子类
function SubFun() {
this.subName = 'Sub';
this.age = 18;
}
SubFun.prototype = new SuperFun();//此时要注意的是SubFun.construction指向的是SuperFun
SubFun.prototype.sayAge = function (params) {
console.log(params ? params :this.age)
}
// 实例
let instance = new SubFun();
console.log('继承了父类的属性', instance.name);// 继承了父类的属性 Super
instance.sayAge();// 18
instance.sayName();// 继承了父类的原型上的方法 Super
instance.colors.push('yellow');
console.log('instance.colors', instance.colors);
// instance.colors [ 'red', 'blue', 'yellow' ]
let instance1 = new SubFun();
instance1.colors.push('black');
console.log('instance1.colors', instance1.colors);
//instance1.colors [ 'red', 'blue', 'yellow', 'black' ] 无法实现多继承
instance instanceof SubFun;// true
instance instanceof SuperFun;// true
SubFun instanceof SuperFun; // false
SubFun.prototype instanceof SuperFun;// true
SubFun.prototype instanceof SubFun;// true
特点:利用原型,让一个引用类型继承另一个引用类型的属性及方法
优点:继承了父类的模板也继承了父类的原型对象
缺点:
- 来自原型对象的所有属性被所有实例共享
- 创建子类实例时,无法向父类构造函数中传递参数
- 无法实现多继承
- 可以在子类构造函数中,为子类实例增加实例属性,如果要新增原型属性和方法,必须在
SubFun.prototype = new SuperFun()
之后加。
借用构造函数继承
基本思想: 在子类构造函数内部调用父类构造函数
// 父类
function SuperFun(name){
this.name = name;
this.colors=['red','blue']
}
SuperFun.prototype.sayName = function () {
console.log(this.name)
}
// 子类
function SubFun() {
SuperFun.call(this, 'Super');//继承了父类,同时还可以向父类传递参数
this.age = 18;
}
// 实例
let instance = new SubFun();
instance.colors.push('yellow');
console.log('instance.colors', instance.colors);
// instance.colors [ 'red', 'blue', 'yellow' ]
let instance1 = new SubFun();
instance1.colors.push('black');
console.log('instance1.colors', instance1.colors);
// instance.colors [ 'red', 'blue', 'yellow' ] 实现了多继承
优点:
- 可以想父类传递参数
- 实现多继承(子类实例共享父类的定义的所有对象的初始化代码) 缺点:
- 只能继承父类的实例属性和方法,不能继承原型属性和方法
- 无法实现函数复用,每个子类都有父类实例函数的副本,影响性能
组合继承
基本思想: 将原型链继承与构造函数继承组合在一起,从而发挥两者职场的一种继承模式。
使用原型链实现对原型属性和方法的继承,实现函数复用;而通过借用构造函数来实现实例属性的继承,保证了每个实力都有自己的属性。
// 父类
function SuperFun(name){
// 在这里定义的是实例属性和实例方法
this.name = name;
this.colors=['red','blue'];
}
SuperFun.prototype.saySupName = function () {
console.log(this.name)
}
// 子类
function SubFun(name, age) {
SuperFun.call(this, name);//继承了父类,同时还可以向父类传递参数(第1次调用)
this.age = age;
}
SubFun.prototype = new SuperFun();// 将父类实例作为子类原型实现函数复用(第2次调用)
SubFun.prototype.constructor = SubFun;// 组合继承需要修复构造函数指向
SubFun.prototype.sayAge = function () {
console.log(this.age);
}
let instance = new SubFun('xm', 18);
instance.saySupName();// xm
instance.sayAge();//18
instance.colors.push('yellow');
console.log('instance.colors', instance.colors);
// instance.colors [ 'red', 'blue', 'yellow' ]
let instance1 = new SubFun('sl', 20);
instance1.saySupName();// sl
instance1.sayAge();// 20
instance1.colors.push('black');
console.log('instance1.colors', instance1.colors);
// instance.colors [ 'red', 'blue', 'yellow' ] 实现了多继承
优点:
- 可以继承实例属性和方法
- 也可以继承原型的属性和方法
- 不存在应用属性共享问题
- 可传参可复用
缺点:
- 调用了两次父类狗在函数,生成了两份实例
原型式继承
这个方法并没有使用严格意义上的构造函数。
基本思想:借助原型可以基于已有的对象创建新对象,同事还不必因此创建自定义类型。
原型式继承本质其实就是个浅拷贝,以一个对象为模板复制出新的对象,属性共享
function object( o ){
var G = function(){};
G.prototype = o;
return new G();
}
var obj = {
name : 'ghostwu',
age : 22,
color: ['red'],
show : function(){
return this.name + ',' + this.age;
}
};
var obj1 = object( obj );
obj1.color.push('blue');
console.log('obj1.color',obj1.color);//['red', 'blue']
var obj2 = object( obj );
obj1.color.push('yellow');
console.log('obj1.color',obj1.color);//['red', 'blue', 'yellow']
console.log('obj2.color',obj2.color);//['red', 'blue', 'yellow']
object
函数中,以对象o
为模板,在object
函数体里面,定义一个构造函数,让构造函数的原型对象(prototype
)指向o
,返回构造函数的一个实例,这样就可以访问到对象o
的所有属性和方法。
在ES5中主要是通过
Object.creat()
方法规范化了原型式继承。这个方法接受两个参数:一个作为新对象原型的对象,另一个(可选)为新对象定义额外属性的对象。在传入一个参数的时候,
Object.creat()
和Object()
方法行为相同。
var obj = {
name: 'gxm',
color : ['red']
};
var obj1 = Object.create( obj );
obj1.color.push( 'yellow' );
var obj2 = Object.create( obj,{
name:{
value: 'xmz'
}
} );
console.log(obj2.name,'---', obj2.color );
//xmz --- ['red','yellow']
var obj3 = Object.create( obj,{
color:{value:['red','blue']}
} );
console.log(obj1.color,obj2.color,obj3.color );
// ['red','yellow'] ['red','yellow'] ['red','blue']
寄生式继承
寄生式继承就是把原型式继承再次封装,然后在对象上扩展新的方法,再把新对象返回
var obj = {
name: 'gxm',
color : ['red']
};
function object(o) {
var G = function () { };
G.prototype = o;
return new G();
}
function createAnother(original) {
let clone = object(original);
clone.sayHi = function(){
console.log('hi');
}
return clone;
}
let anotherObj = createAnother(obj);
anotherObj.sayHi();// hi
寄生组合式继承
在组合继承中,调用了两次父类构造函数,这里通过寄生方式,砍掉父类的实例属性,这样,在调用两次父类的构造函数的时候,就不会初始化两次实例方法和实例属性,避免了组合继承的缺点。
基本思想: 借用构造函数继承实例属性,通过原型链的混成形式来继承原型方法
// 父类
function SuperFun(name){
this.name = name;
this.colors=['red','blue'];
}
SuperFun.prototype.saySupName = function () {
console.log(this.name)
}
// 子类
function SubFun(name, age) {
SuperFun.call(this, name);//继承了父类,同时还可以向父类传递参数
this.age = age;
}
SubFun.prototype = Object.create(SuperFun.prototype);// 将父类实例作为子类原型实现函数复用
SubFun.prototype.constructor = SubFun;// 组合继承需要修复构造函数指向
SubFun.prototype.sayAge = function () {
console.log(this.age);
}
let instance = new SubFun('xm', 18);
优点:
- 调用两次
SuperFun
构造函数,只创建一份父类属性 - 原型链保持不变
- 能正常使用
instancof
和isPrototypeOf
寄生组合式继承是集寄生式继承和组合继承的优点于一身,是实现基于类型继承的最有效的方式。
如有错误,请留言,Thanks♪(・ω・)ノ