js继承

143 阅读4分钟

「这是我参与2022首次更文挑战的第3天,活动详情查看:2022首次更文挑战」。

1面向对象继承(类和实例)

表现为 子类继承父类 主要是为实例和原型增加方法的 多态(重写,重载)

1.1 原型链继承

将子类 B 的原型对象 重写成父类 A 的一个实例

function A() {
}
A.prototype.age = 19;
A.prototype.say = function () {
   console.log('来自A类原型的say方法')
};
function B() {}
B.prototype = new A();  把B类的原型改写成一个A类的实例;
let b = new B();

把B的原型改写成A类的一个实例,此时通过 b.age 访问b的age属性,首先在私有属性中查找,私有属性中没有age属性,接着去b所属类的原型(B.prototype)上查找,此时原型对象是A的实例对象,在原型对象也没有age属性,然后通过原型对象的proto就找到了A.prototype上,A的prototype上有age属性

原型继承是把子类公有的属性和私有的属性都变成了子类私有的属性;

==缺点==:改写子类的原型对象,会导致子类原型对象上的constructor属性被改写,需要重新指定继承后的constructor;

1.2 借用构造函数继承

function A() {
   this.a = 'aa';
   this.say = function () {
      console.log('A say');
   }
}
A.prototype.public = 'public';
function B() {
   A.call(this);  this是B类的一个实例,A.call(this) 的意思是把A中的this就该成B的实例(而在B的构造函数中this就是B的实例)这样在A中通过this.xxx = xxx 的方式添加的属性都会添加到B的实例身上。
}
let b = new B();
console.log(b);  {a: 'aa', say: fun.....}
let b2 = new B();
console.log(b2.say === b.say);  false;
借用构造函数继承:

把父类当做普通函数,在子类的函数体里面,通过call方法执行 A.call(this) call方法是用来修改this指向的,这样一来就把A中的this修改成了b的实例;在函数A中通过this.xxx = xxx 添加的属性都添加到了B的实例身上;

==特点==:只能把父类的私有属性和方法继承为子类的私有属性和方法;

1.3组合继承

组合继承:原型链继承 + 借用构造函数继承

原型链继承:把父类私有的和共有的继承为子类公有的; 借用构造函数继承:把父类私有的继承为子类私有的

function A() {
   this.a = '私有的'
}
A.prototype.text = '公有';
A.prototype.say = function () {
   console.log('A公有的say方法')
};
function B() {
   A.call(this);  借用构造函数继承,继承父类私有的
}
B.prototype = new A();  原型链继承,继承父类私有和公有的属性;
B.prototype.constructor = B;
let b = new B();
console.log(b.text);  公有的
console.log(b.a);  私有的

==缺点:== 组合继承也并非没有缺点,组合继承会父类的私有继承两次,一份在借用构造函数继承时成为私有的,而另一份是在原型继承时成为公有的;

1.4 原型式继承

原型式继承:把父类的公有属性继承为子类的公有属性; 创建一个新的对象,并且新对象的proto指向A.prototype,最后把这个新对象作为B类的原型;

function A() {
   this.private = 'private私有';
}
A.prototype.public = 'public公有';
function B() {}
B.prototype = Object.create(A.prototype);  创建一个指定原型的对象 创建一个对象,并且这个对象的__proto__ 指向A.prototype
B.prototype.constructor = B;  原型式继承同样是修改B类原型的指向,所以需要重新指定构造函数
let b = new B();
console.log(b.public);

1.5 寄生组合继承

寄生组合式继承:原型式继承 + 借用构造函数继承

function A() {
   this.private = '私有属性';
}
A.prototype.public = '公有属性';
function B() {
   A.call(this);  借用构造函数继承
   this.name = 'b私有的';
}
 原型式继承:把父类公有的 继承为子类实例公有的
B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;
let b = new B();  {name: '私有的', private: '私有属性'}
console.log(b.private);  继承过来的私有属性
console.log(b.public);  继承过来到的公有属性

1.6 冒充对象继承

冒充对象继承:在子类的构造函数中生成一个父类的实例,把父类的这个实例进行遍历,把属性都添加子类的实例上;

function A() {
   this.private = '私有属性';
}
A.prototype.public = '公有属性';
function B() {
   this.name = 'B私有的属性';
   let tmp = new A();
   for (let key in tmp) {
      this[key] = tmp[key];
   }
}
let b = new B();
console.log(b);

1.7 Es6 继承(extends 关键字)

原理:寄生组合式继承

class A {
   constructor (name, age) {
      this.name = name;
      this.age = age;
   }
    公有方法(添加到原型上)
   say () {
      console.log(`${this.name} say`);
   }
}
 ES6继承时使用 extends 关键字实现继承
class B extends A {  B继承A类
   constructor (x, y, forName, forAge) {
       注意:在使用ES6extends关键字之前,必须使用super(); super表示父类的构造函数
      super(forName, forAge);  注意:
      this.x = x;
      this.y = y;
   }
}
let b = new B('x', 'y', 'mabin', 18);
console.log(b);
b.say();