一、原型链继承、构造函数继承、两者组合继承
-原型链继承
让父类的实例作为子类的原型,将子类的原型构造器补充完整 (为了让子类继承方法)
做法:
(1)让子类的原型是父类的一个实例;
(2)手动给字类的原型添加构造器
function Father(name,age){
this.name = name;
this.gender = 'male';
this.num = ['1']
}
Father.prototype.sayAge = function(){
console.log(this.age)
}
function Child(age){
this.age = age
}
Child.prototype = new Father(); // 子类的原型是父类的实例对象
Child.prototype.constructor = Child;
//子类可以重写父类的方法
//Child.prototype.sayAge = function(){
// console.log('12')
//}
var c1 = new Child(20)
console.log(c1.name) // undefined
c1.num.push('2')
c1.sayAge() // 子类的实例化对象继承了父类的方法
var c2 = new Child(20)
console.log(c2.num) // ['1','2']
原型继承导致的问题:
问题一 :当实例化之后,实例修改原型属性时,会影响其他的实例属性,通过上面的例子,我们发现,虽然没有给c2.num执行操作,但还是收之前实例化操作的影响,假如说你就是为了实现这样,那当然没问题,多数情况下,我们不希望各个实例之间相互影响。
问题二 :我们创建子类型实例的时候,不能给父类型的构造函数传递参数,或者说,在不影响其他实例的情况下不能传参数。
因此,我们引出了第二种继承方式:借用构造函数的方式
-构造函数继承
在子类当中去调用父类的构造函数(为了让子类继承属性)
function Father(name,age){
this.name = name;
this.num = ['1']
this.sayHi = function(){
console.log('hello')
}
}
function Child(){
Father.call(this)
}
var c1 = new Child()
console.log(c1.name) // undefined
c1.num.push('2')
console.log(c1.num) // ['1','2']
c1.sayHi() // 可以使用父类的方法,但是不能复用
var c2 = new Child(20)
console.log(c2.num) // ['1']
通过这种方式,每一个实例都有自己的一份属性和方法,不受其他的实例所影响,我们想到代码的复用问题,所以这种方式还是不完美,接着引出下一种继承方式:组合继承
组合继承就是把前面的两者中方式结合起来,让属性值不受影响,让方法复用
-两者组合继承
Function Father(name){
this.name = name;
this.num = ['1'];
}
Father.prototype.sayName = function(){ //父类原型的方法
console.log(this.name);
}
function Child(age,name){
Father.call(this,name);//继承属性
this.age = age;
}
Child.prototype = new Father();//继承方法
Child.prototype.constructor = Child;
Child.prototype.sayAge = function(){ // 自身原型的方法
console.log(this.age);
}
var c1 = new Child(20,"Bob");
c1.sayage(); //20
c1.sayname(); //"Bob"
c1.num.push(2);
console.log(c1.num)// [ '1' , '2' ]
var c2 = new Child(12,"Lili");
c2.sayage();//12
c2.sayname();//"Lili"
console.log(c2.num)// [ '1' ]
二、class继承
class Father{
//在constructor里添加--实例属性
constructor(name,age){
this.name = name;
this.age = age;
}
//实例方法——相当于:Person.Prototype.sayHi
sayHi(){
console.log('Hi')
}
}
//使用extends 就必须继承属性,必须手动写super(),否则报错;
//使用extends 就自动继承了父类的方法
class Child extends Father{
constructor(name,age,gender){
super(name,age); // 这是继承的属性
this.gender = gender
}
//重写方法
sayHi(){
console.log('Hello')
}
}
var c1 = new Child('Bob',12,'male')
console.log(c1) // Child {name: "Bob", age: 12, gender: "male"}
c1.sayHi() //'Hello'
三、原型式继承 、寄生式继承,寄生组合继承
-原型式继承
var person = {
name:'Bob',
hobbies:['打篮球','唱歌']
}
function object(o){
function F (){};
F.prototype = o;
return new F();
}
var obj1 = object(person) // obj1 是 F 的实例化对象
console.log(obj1.name) // Bob
obj1.hobbies.push('跳舞')
var obj2 = object(person)
console.log(obj2.name) // Bob
console.log(obj2.hobbies) // ["打篮球", "唱歌", "跳舞"]
- 从本质上讲, object() 对传入其中的对象执行了一次浅复制,
obj1.hobbies.push
之后影响了obj2.hobbies
- obj1 是 F 的实例化对象;obj1. __ proto __ === F.prototype === person
object.create()
只有一个参数时功能与上述object()
方法相同 ; 它的第二个参数与Object.defineProperties()方法的第二个参数格式相同: 每个属性都是通过自己的描述符定义的.以这种方式指定的任何属性都会覆盖原型对象上的同名属性.
var person = {
name : "Van"
};
var anotherPerson = Object.create(person, {
name : {
value : "Louis"
}
});
alert(anotherPerson.name);//"Louis"
-寄生式继承
寄生式继承是在原型式继承的基础上,用类似于工厂模式的方式,增强子类对象,即为子类添加属性和方法
var person = {
name:'Bob',
hobbies:['打篮球','唱歌']
}
function object(o){
function F (){};
F.prototype = o;
return new F();
}
function createAnother(o){
var newobj = object(o); //创建对象
newobj.sayname = function(){ //增强对象
console.log(this.name);
}
return newobj;//返回这个对象
}
var ob1 = createAnother(person); //ob1 是F的实例化对象
ob1.sayname();// Bob
console.log(obj1)
新对象newobj
不仅具有 person
的所有属性和方法, 而且还被增强了, 拥有了sayname()
方法.
注意: 使用寄生式继承来为对象添加函数, 会由于不能做到函数复用而降低效率;这一点与构造函数模式类似.
-寄生组合继承
前面讲过,组合继承是 JavaScript 最常用的继承模式; 不过, 它也有自己的不足. 组合继承最大的问题就是无论什么情况下,都会调用两次父类构造函数: 一次是在创建子类型原型的时候, 另一次是在子类型构造函数内部.
寄生组合式继承就是为了降低调用父类构造函数的开销而出现的 .
//寄生继承 创建一个新的对象返回一个新实例
function object(o){
function F(){}
F.prototype = o;
return new F();
}
//父层,超级层
function Father(name){
this.name = name;
this.hobbies = ["打篮球","唱歌"];
}
Father.prototype.sayName = function(){
console.log(this.name);
}
function Child(name,age){
Father.call(this,name);//用构造函数方法继承属性
this.age = age;
}
//继承父层的prototype
function inheritPrototype(child,father){
var newObj = object(father.prototype);//返回一个新实例(对象)
newObj.constructor = child;//增强对象
child.prototype = newObj; //指定对象
}
//继承父层的原型
inheritPrototype(Child,Father);
Child.prototype.sayAge = function(){
console.log(this.age);
}
var c1 = new Child("bob",20);
c1.hobbies.push("跳舞");
c1.sayName();//bob
c1.sayAge();//20
var c2 = new Child("lili",23);
c2.sayName();//lili
c2.sayAge();//23
console.log(c2.hobbies); //["打篮球", "唱歌"]
关系图如下:
参考文章
- js原型链继承,借用构造函数继承,组合继承,寄生式继承,寄生组合继承
- JS原型链与继承别再被问倒了