JS继承几种方式
一 原型链继承:核心: 将父类的实例作为子类的原型(父类子类都是构造函数,通过类创建对象)
- 优点:方法复用
方法定义在父类的原型上,可以复用父类构造函数的方法,比如say方法。 - 缺点:
- 创建子类实例时,无法传父类参数
- 引用类型的值的原型会被所有实例共享
- 继承单一,无法实现多继承(在JavaScript中继承是依赖于原型prototype链实现的,只有一条原型链,理论上是不支持继承多个父类的,可以通过变通的方式实现类似功能)
1 function SuperType(){ 2 this.property=true; 3 } 4 SuperType.prototype.getSuperValue=function(){ 5 return this.property; 6 }; 7 function SubType(){ 8 this.subproperty=false; 9 } 10 //继承了SuperType 11 SubType.prototype=new SuperType(); 12 13 SubType.prototype.getSubValue=function(){ 14 return this.subproperty; 15 }; 16 var instance=new SubType(); 17 console.log(instance.getSuperValue()); //true
1 function SuperType(){ 2 this.colors=["red","blue"]; 3 } 4 function SubType(){ 5 } 6 //继承了SuperType 7 SubType.prototype=new SuperType(); 8 var ins1=new SubType(); 9 ins1.colors.push("black") //"red,blue,black" 10 var ins2=new SubType(); 11 console.log(ins2.colors); //"red,blue,black"
二 构造函数继承:核心:借用父类构造函数来增强子类实例,等于是复制父类的实例属性给子类
优点:实例之间独立
- 创建子类实例,可以向父类构造函数传参
- 子类实例不共享父类构造函数的引用属性,如arr
- 可实现多继承(通过多个call或apply继承多个父类)
缺点:
- 父类方法不能复用
- 由于方法在父构造函数中定义,导致方法不能复用(每次创建子类实例都要创建一遍方法)
- 子类实例继承不了父类原型上的属性,因为没有用到原型
function Parent(name) { this.name = name; //实例基本属性(该属性,强调私有,不共享) this.arr = [1]; // (该属性,强调私有) this.say = function () { //实例引用属性(该属性,强调复用,需要共享) console.log('hello'); } } function Child(name, like) { console.log(this); Parent.call(this, name); //核心,拷贝了父类的实例属性和方法 this.like = like; } let boy1 = new Child('小刚', 'apple'); let boy2 = new Child('小明', 'orange'); //优点1:可向父类构造函数传参 console.log(boy1.name, boy2.name); //小刚,小明 //优点2:不共享父类构造函数的引用属性 boy1.arr.push(2); console.log(boy1.arr, boy2.arr); //[1,2],[1] //缺点1:方法不能复用 console.log(boy1.say === boy2.say); //false (说明boy1和boy2的say方法独立,不是共享的) //缺点2:不能继承父类原型上的方法 Parent.prototype.walk = function () { console.log('我会走路'); } boy1.walk; //undefined(说明实例不能获得父类原型上的方法)
三 拷贝继承:
四 组合继承:核心:通过调用父类构造,继承父类的属性并保留传参的优点,然后通过原型链继承将父类实例作为子类原型,实现函数复用
优点:
- 父类的方法定义在原型对象上,可以实现方法复用
- 创建子类实例,可以向父类构造函数传参;并且不共享父类的引用属性,如arr
缺点:由于调用了2次父类的构造方法,会存在一份多余的父类实例属性
五 原型式继承:
缺点:引用类型值会共享,值类型不会共享,因此在改变值类型时,相当于给自己添加了属性。
function object(o){ function F(){} //F.prototype={name:'ccdida',friends:['shelly','Bob']} F.prototype=o // new F() //F是个构造函数,返回F的实例:1.this此时用不上 2.将实例的__proto__指向F.prototype. //即返回了一个实例,其__proto__指向{name:'ccdida',friends:['shelly','Bob']} return new F() } var person={ name:'ccdida', friends:['shelly','Bob'] } var person1=object(person) var person2=object(person) //object函数相当于实现了Object.Create的功能 console.log(person1.__proto__===person) //true person2.friends.push('shlimy') console.log(person1.friends)// ["shelly", "Bob", "shlimy"]
六 寄生组合继承:核心:通过寄生方式,砍掉父类的实例属性,这样,在调用两次父类的构造的时候,就不会初始化两次实例方法/属性,避免的组合继承的缺点
1 function Parent(name) { 2 this.name = name; //实例基本属性(该属性,强调私有,不共享) 3 this.arr = [1]; //实例引用属性(该属性,强调私用,不共享) 4 } 5 Parent.prototype.say = function () { //将需要复用、共享的方法定义在父类原型上 6 console.log('hello'); 7 } 8 function Child(name, like) { 9 Parent.call(this, name); //核心 10 this.like = like; 11 } 12 //核心 通过创建中间对象,子类原型和父类原型就会隔离开,不再是同一个,有效避免了方式4的缺点 13 Child.prototype = Object.create(Parent.prototype); 14 15 Child.prototype.constructor = Child;//修复代码 16 17 let boy1 = new Child('小刚', 'apple'); 18 let boy2 = new Child('小明', 'orange'); 19 let p1 = new Parent('小爸爸'); 20 21 console.log(boy1.constructor, p1.constructor); //修复之后:Child,Parent 22 23 24 // Object.create()函数等价为:下面函数即是原型式继承实现 25 function object(o) { 26 function F() { } 27 F.prototype = o; 28 return new F(); 29 } 30 31 // 于是中间那段核心代码可改为: 32 function object(o) { 33 function F() { } 34 F.prototype = o; 35 return new F(); 36 } 37 Child.prototype = object(Parent); 38 39 Child.prototype.constructor = Child;
JS继承的实现方式:https://www.cnblogs.com/humin/p/4556820.html
JS五种继承方法和优缺点:https://blog.csdn.net/m0_51235736/article/details/113795449