js中原型链实现继承——最佳实践

js中的继承核心思想:就是通过原型(prototype)来继承属性和方法。

1、原型链继承

通过prototype继承父类的属性和方法

ini 复制代码
function Parent1() {
  this.name = "qmq";
  this.arr = [1, 2, 3];
}
Parent1.prototype.getName = function () {
  console.log(this.name);
};
function Child(age) {
  this.age = age;
}
Child.prototype = new Parent1(); // 继承父类的属性和方法
Child.prototype.constructor = Child; // 修正constructor指向

const obj1 = new Child(18);
const obj2 = new Child(19);
obj1.arr.push(4);
console.log(obj1.arr, obj2.arr); // [1, 2, 3, 4] [1, 2, 3, 4]

通过Child.prototype = new Parent1();继承了父类构造函数中的属性和方法,但是这样会造成两个问题:

1、父类中的arr引用类型多个实例对象之间会互相影响。因为创建child实例对象时,它的prototype都是等于new Parent1()。

2、没法向父类构造参数传参。传参需要函数作为接口,这个写法没有传参的空间。

2、构造函数继承

由此可以想到通过函数传参,引申出第二种构造函数继承,不仅解决了传参问题,还通过call改变了this的指向,解决了引用类型相互影响的问题,因为创建了不同的child实例,分别都继承了各自的父类构造函数上的属性或方法,不会互相影响。

ini 复制代码
function Parent2(name) {
  this.name = name;
  this.arr = [1, 2, 3];
  this.setName = function () {
    console.log("setName", this.name);
  };
}
function Child2(age, name) {
  Parent2.call(this, name); //  通过call改变this指向,继承父类的属性
  this.age = age;
}
const obj1 = new Child2(12, "qmq");
const obj2 = new Child2(13, "qmq1");
console.log(obj1.setName === obj2.setName); // false

但又出现了新的问题

1、没法继承父类原型上的属性或方法,因为用的是构造函数去访问父类中的属性,不涉及到原型

2、父类中的方法无法复用,也是生成了多个副本。

3、组合继承

结合原型继承+构造函数继承这两种继承的优缺点

ini 复制代码
function Parent3() {
  this.name = "qmq";
  this.arr = [1, 2, 3];
  this.setName = function () {
    console.log(this.name);
  };
}
Parent3.prototype.getName = function () {
  console.log(this.name);
};
function Child3(age, name) {
  Parent3.call(this, name); //  通过call改变this指向,继承父类的属性
  this.age = age;
}
Child3.prototype = new Parent3(); // 继承父类的属性和方法
Child3.prototype.constructor = Child3; // 继承父类的构造函数

const obj1 = new Child3(12, "qmq");
const obj2 = new Child3(13, "qmq1");
obj1.arr.push(4);
console.log(obj1.arr); // [1, 2, 3, 4]
console.log(obj2.arr); // [1, 2, 3]
console.log(obj1.setName === obj2.setName); // false

这样写,解决了以上4个问题:

1、能传参,用了Parent3.call(this, name);

2、能继承父类属性和方法,Child3.prototype获取了父类上的属性方法

3、父类引用类型不会互相影响,Parent3.call(this, name);通过call修改了this指向

4、父类中原型上的方法可复用

但还有个小问题,就是父类构造函数会被执行两次,第一次是Child3.prototype = new Parent3();创建子类实例的时候,第二次是Parent3.call(this, name);子类中去改变父类this指向的时候,那么就去改掉其中一个,就只剩一个了,把Child3.prototype = new Parent3();改成 Child3.prototype = Object.create(Parent3.prototype),这样既获得了父类的属性和方法,也避免了重复调用父类构造函数。

最终,贴一下最佳实践的代码,即寄生组合继承。

4、寄生组合继承
ini 复制代码
function Parent4(name) {
  this.name = name;
  this.arr = [1, 2, 3];
}
Parent4.prototype.getName = function () {
  console.log(this.name);
};
function Child4(age, name) {
  Parent4.call(this, name);
  this.age = age;
}
Child4.prototype = Object.create(Parent4.prototype); // 继承父类的属性和方法
Child4.prototype.constructor = Child4; // 修正constructor指向

const obj1 = new Child4(18, "qmq2");
const obj2 = new Child4(19, "qmq2");
obj1.arr.push(4);
console.log(obj1.arr, obj2.arr); // [1, 2, 3, 4] [1, 2, 3, 4]
console.log(obj1.getName === obj2.getName);
相关推荐
小帆聊前端2 分钟前
深度解读虚拟列表:从原理到实战,解决长列表渲染性能难题
前端·javascript
在下历飞雨2 分钟前
Kuikly基础之动画实战:让孤寡青蛙“活”过来
前端·ios·harmonyos
2***c4356 分钟前
nginx服务器实现上传文件功能_使用nginx-upload-module模块
服务器·前端·nginx
p***930310 分钟前
Java进阶之泛型
android·前端·后端
木易 士心13 分钟前
Element UI 多级菜单缩进的动态控制:从原理到工程化实践
前端·vue.js·ui
狮子座的男孩16 分钟前
js函数高级:03、详解原型与原型链(原型、显式原型与隐式原型、原型链、原型链属性、探索instanceof、案例图解)及相关面试题
前端·javascript·经验分享·显示原型与隐式原型·原型链及属性·探索instanceof·原型与原型链图解
烛阴18 分钟前
C#继承与多态全解析,让你的对象“活”起来
前端·c#
狗哥哥20 分钟前
Swagger对接MCP服务:赋能AI编码的高效落地指南
前端·后端
zl_vslam22 分钟前
SLAM中的非线性优-3D图优化之相对位姿Between Factor(六)
前端·人工智能·算法·计算机视觉·slam se2 非线性优化
申阳22 分钟前
Day 18:01. 基于 SpringBoot4 开发后台管理系统-快速了解一下 SpringBoot4 新特性
前端·后端·程序员