参考链接: js继承的6种方式
6种方法
1、原型链继承
核心:将父类的实例作为子类的原型
//父类
function superType(){
this.name='father';
this.colors= ['red','blue','green'];
}
//方法
superType.prototype.getName=function(){
console.log(this.name);
console.log(this.colors);
}
//子类
function subType(){
// this.name='son'; 如果加上这句x.getName会输出son
}
//实质:把superType的实例赋值给subType的原型(实例包含指向原型对象的指针)
//于是superType中的所有属性和方法,subType都有了。
subType.prototype = new superType();
console.log('原型链');
var x = new subType();//创建实例
var y = new subType();
x.getName();//输出father
y.name='s';
y.getName();//输出s
y.colors=['1','2','3'];//这是深拷贝(maybe)所以y改变了x不会改变
x.getName();//father ['red','blue','green'];
y.getName();//s ['1','2','3']
//如果使用y.colors.push('a');则x和y的值会一样,即共享同一个引用类型属性
原型链的缺点:(所以其实实践过程中很少使用)
1.所有subType的实例共享一个superType的引用类型属性
2.创建subType时没办法向superType传参
实例可以继承的属性有:
实例的构造函数属性,父类原型的属性、父类构造函数属性
特点:
- 非常纯粹的继承关系,实例是子类的实例,也是父类的实例
- 父类新增原型方法/原型属性,子类都能访问到
- 简单,易于实现
缺点:
- 可以在构造函数中,为实例增加实例属性。如果要新增原型属性和方法,则必须放在new superType()这样的语句之后执行。
- 无法实现多继承
- 致命缺陷:来自原型对象的所有属性被所有实例共享
- 致命缺陷:创建子类实例时,无法向父类构造函数传参
2、构造继承(经典继承)
核心:使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类
function Person(name){
this.name=name;
this.colors= ['red','blue','green'];
}
Person.prototype.getName=function(){
console.log(this.name);
}
//子类
function Child(){
Person.call(this,'a');//调用父类的构造函数实现继承效果
}
//实例
var child1 = new Child();
var child2 = new Child();
console.log('构造函数');
child1.colors.push('white');
console.log(child1.name);
// child2.getName();不能通过,因为这是父类的原型的方法
console.log(child1.colors);//['red', 'blue', 'green', 'yellow']
console.log(child2.colors);//['red', 'blue', 'green']特点:
- 解决了1中,子类实例共享父类引用属性的问题
- 创建子类实例时,可以向父类传递参数
- 可以实现多继承(call多个父类对象)即继承多个构造函数属性
缺点:
- 实例并不是父类的实例,只是子类的实例
- 只能继承父类的实例属性和方法,不能继承父类原型属性/方法
- 无法实现函数复用,每个子类都有父类实例函数的副本,影响性能
3、组合继承(组合原型链继承和借用构造函数继承)(常用)
核心:结合了两种模式的优点,传参和复用
使用原型链实现对原型方法的继承,而通过借用构造函数来实现对实例属性的继承
function Parent(name){
this.name=name;
this.arr=['red','blue'];
}
Parent.prototype.getName=function(){
console.log(this.name);
}
//构造函数的方法
function child(name,age){
Parent.call(this,name);//第二次调用Parent()
this.age=age;
}
//原型链继承的方法
child.prototype = new Parent();//第一次调用Parent()
var c1=new child('hhh',18);
var c2=new child('yyy',19);
console.log('组合继承');
c1.getName();//hhh
c2.getName();//yyy
console.log(c1.age);//18
console.log(c2.age);//19
c1.arr.push('green');
console.log(c1.arr);//['red', 'blue', 'green']
console.log(c2.arr);//['red', 'blue']
console.log(c1 instanceof child);//true
console.log(c1 instanceof Parent);//true特点:
- 可以继承父类原型上的属性,可以传参,可以复用
- 每个新实例引入的构造函数属性是私有的
缺点
- 覅用了两次父类构造函数(耗内存),子类的构造函数会代替原型上的那个父类构造函数。
4、原型式继承
核心:用一个函数包装一个对象,然后返回这个函数的调用,这个函数就变成了可以随意增添属性的实例或对象。object.create()就是这个原理
function createObj(o){
function f(){}//在内部创建一个临时构造函数,
f.prototype = o;//将传入的对象作为这个构造函数的原型,
return new f();//最后返回这个临时类型的新实例
}
var person={//父类
name:'xiao',
friend:['ddd','ccc']
};
var anotherPerson = createObj(person);
var anotherPerson2 = createObj(person);
console.log('原型式继承');
anotherPerson.name='hscod';//给anotherPerson添加了name值,但是并没有修改原型上的name值
anotherPerson.friend.push('aaa');
console.log(anotherPerson);//{name: 'hscod'}
anotherPerson2.friend.push('bbb');
console.log(anotherPerson2);//{}
console.log(person.friend);//[ 'ddd', 'ccc', 'aaa', 'bbb' ]
//缺点和原型链继承一样,所有的引用类型会被共享特点:类似于复制一个对象,用函数来包装。
缺点:1、所有实例都会继承原型上的属性。
2、无法实现复用。(新实例属性都是后面添加的)
5、寄生式继承
//创建一个仅用于封装继承过程的函数,该函数在内部以某种形式来做增强对象,最后返回对象。
//可以理解成在原型式继承的基础上新增一些函数或属性
console.log('寄生式继承');
var ob1=createObj(person);
var ob2=Object.create(person);//ES5的规范写法
console.log(ob1.name);//xiao
console.log(ob2.name);//xiao
function createSon(s){//子类
var newob=createObj(s);//创建对象
newob.sayName = function(){//增强对象
console.log(this.name);
}
return newob;//指定对象
}
var p1=createSon(person);
p1.sayName();//xiao
//缺点:和构造函数一样,每次创建对象都会创建一遍方法重点:就是给原型式继承外面套了个壳子。
优点:没有创建自定义类型,因为只是套了个壳子返回对象(这个),这个函数顺理成章就成了创建的新对象。
缺点:没用到原型,无法复用。
6、寄生组合式继承(常用)
寄生:在函数内返回对象然后调用
组合:1、函数的原型等于另一个实例。2、在函数中用apply或者call引入另一个构造函数,可传参
//子类构造函数复制父类的自身属性和方法,子类原型只接受父类的原型属性和方法
function pa(name){
this.name=name;
this.attr=['a','b'];
}
pa.prototype.speak = function(){
console.log('my name is '+this.name);
}
function ch(name,age){
pa.call(this,name);
this.age=age;
}
function createOb(o){
function F(){};
F.prototype =o;
return new F();
}
//Child.prototype = new Parent();
function inheritPrototype(child,parent){
var prototype = createOb(parent.prototype);//创建对象
prototype.constructor=child;//增强对象
child.prototype=prototype;//指定对象
}
inheritPrototype(ch,pa);
console.log('寄生组合式');
var ch1=new ch('zyy','18');
console.log(ch1);//ch { name: 'zyy', attr: [ 'a', 'b' ], age: '18' }
console.log(ch1 instanceof ch);//true
console.log(ch1 instanceof pa);//true
//优点:只调用了一次pa构造函数,因此避免了在pa.prototype上面创建不必要的多余的属性。
京公网安备 11010502036488号