JavaScript进阶:类式继承和构造函数继承
2022-01-11 本文已影响0人
听书先生
1、类式继承
类的原型对象的作用就是为类的原型添加共有方法,但是类不能直接访问这些属性和方法,必须通过原型prototype
来访问,而我们实例化一个父类的时候,新创建的对象复制了父类的构造函数内的属性和方法并且将原型__proto__
指向了父类的原型对象,这样就拥有了父类的原型对象上的属性和方法,并且这个新创建的对象可以直接访问父类原型对象上的属性与方法。
当把父类赋值到子类的原型对象上去时,子类的原型就可以去访问父类原型对象上的属性和方法以及父类构造函数中复制的属性和方法。这便是类式继承的原理。
![](https://img.haomeiwen.com/i25524960/aa4ec4791386a297.png)
- 代码举例:
// 创建一个父类
function ParentClass() {
this.userName = 'e-kr';
}
// 在父类的原型对象上增添共有方法
ParentClass.prototype.getUserName = function() {
return this.userName;
}
// 创建一个子类
function ChildClass() {
this.cusName = 'c-af'
}
// 将父类赋值到子类的原型上去
ChildClass.prototype = new ParentClass();
ChildClass.prototype.getCusName = function() {
return this.cusName;
}
// 实例化子类对象
const child = new ChildClass();
// 子类的实例对象调用父类的方法
const cusName = child.getCusName(); // 'c-af'
const userName = child.getUserName(); // 'e-kr'
// instanceof
const ralation = child instanceof ParentClass; // true
const ralate = ChildClass.prototype instanceof ParentClass; // true
-
instanceof检测:
instanceof是通过判断对象的prototype原型链来确定这个对象是否是属于这个类的实例,而不关心对象与类的自身的结构。(也就是说是判断前面的对象是否是后面类的实例,也就是说并不指二者之间存在继承) -
类式继承的缺陷:
由于子类通过其原型prototype对父类实例化,继承了父类,所以说,子类的实例对象在使用父类的引用属性时,只要一个子类的实例化对象修改了父类的引用类型,其他的子类的实例化对象拿到的都将是修改后的引用类型数据,也就是说,存在了数据共享,开发中就会导致问题。
举例:
function ParentClass() {
this.userName = 'e-kr';
this.array = ['react', 'vue', 'angular'];
}
ParentClass.prototype.getUserName = function() {
return this.userName;
}
function ChildClass() {
this.cusName = 'c-af'
}
ChildClass.prototype = new ParentClass();
ChildClass.prototype.getCusName = function() {
return this.cusName;
}
const child1 = new ChildClass();
const child2 = new ChildClass();
console.log(child2.array); // child1修改引用类型之前child2拿到的数据
child1.array.push('jquery');
console.log(child2.array); // child1修改引用类型之后child2拿到的数据
![](https://img.haomeiwen.com/i25524960/a3dca7f01206ce65.png)
2、构造函数继承
通过call()方法更改函数的作用环境,在子类中调用这个方法将子类中的变量在父类中执行一遍,由于父类中是给this绑定属性的,因此子类也就继承了父类的共有属性,由于该继承方法没有通过prototype继承,因此子类修改父类的引用类型数据不会修改父类中的。
function SuperClass(name) {
this.name = 'e-kr'; // 在父类中写死name,测试子类中的name是否经过此次赋值操作
this.array = ['react', 'vue', 'angular'];
}
function SuberClass(name) {
SuperClass.call(this, name); // 将子类中的name仍进父类中跑一遍(进行一次变量赋值)
}
SuperClass.prototype.getSuperName = function() {
return this.name;
}
const sup = new SuperClass('e-af');
console.log(sup.getSuperName());
console.log(sup.name);
const sub1 = new SuberClass('e-gp');
const sub2 = new SuberClass('e-ar');
console.log(sub1.name); // 同样的打印的e-kr, 说明子类的name被放在父类中执行了一遍
//console.log(sub1.getSuperName()); // sub1.getSuperName is not a function
console.log(sub1.array); // sub2修改引用类型之前sub1拿到的数据
sub2.array.push('demo');
console.log(sub1.array); // sub2修改引用类型之后sub1拿到的数据
![](https://img.haomeiwen.com/i25524960/e16d16434ddb6443.png)
打印出来的结果是未受影响的,可以看出这个继承方式是解决了上一个类式继承的缺陷,但是,这个继承方式也引申了一个更严重的问题,同时也就是构造函数继承的精髓的部分
SuperClass.call(this, name);
,目前是将变量放入父类中执行,因此,这种方法是无法继承到父类的方法的。因此,我们在子类实例化对象的时候尝试着去拿,是出现报错,提示not a function。