JS怎么实现类继承?手把手教你轻松搞定类继承
时间:2025-06-23 11:41:29 137浏览 收藏
想搞懂JS类继承?这篇教程手把手教你!JavaScript类继承是子类复用父类属性和方法并扩展自身特性的关键机制,主要通过原型链实现。本文深入解析原型链继承,例如将子类原型指向父类实例,并介绍如何借助构造函数继承实例属性。ES6的`class`和`extends`语法糖简化了继承,`super`关键字用于调用父类构造函数和方法。同时,文章还强调了避免原型链污染的重要性,分享了使用`Object.create(null)`、Map/WeakMap等安全实践。此外,还探讨了通过混入和组合模拟多重继承的方法,助你灵活运用各种技巧,写出更健壮的JS代码。
JavaScript中的类继承本质是子类复用父类属性和方法并扩展自身特性,主要通过原型链实现,例如将子类原型指向父类实例,并借助构造函数继承实例属性;ES6引入class和extends语法糖简化了继承逻辑,使用super调用父类构造函数和方法;避免原型链污染需不修改内置对象原型、使用Object.create(null)创建无原型对象或Map/WeakMap存储数据、验证用户输入等;super关键字用于调用父类构造函数和访问父类方法;多重继承可通过混入(合并多个类的属性和方法)或组合(通过对象组合功能模块)模拟实现。
JavaScript中的类继承,本质上就是让子类能够复用父类的属性和方法,同时还能扩展自己的特性。实现方式有很多种,各有优劣,没有绝对完美的方案,选择哪种取决于具体的应用场景和个人偏好。

解决方案

最常见的实现方式是基于原型链。简单来说,就是将子类的原型指向父类的实例。这样,子类就可以通过原型链访问到父类的属性和方法。

function Parent(name) { this.name = name; } Parent.prototype.sayHello = function() { console.log("Hello, I'm " + this.name); }; function Child(name, age) { Parent.call(this, name); // 借用构造函数,继承父类的实例属性 this.age = age; } // 核心:将子类的原型指向父类的实例 Child.prototype = Object.create(Parent.prototype); Child.prototype.constructor = Child; // 修正 constructor 指向 Child.prototype.sayAge = function() { console.log("I'm " + this.age + " years old."); }; const child = new Child("Alice", 10); child.sayHello(); // 输出: Hello, I'm Alice child.sayAge(); // 输出: I'm 10 years old.
这种方式的优点是简单易懂,兼容性好。缺点是子类实例共享父类实例的属性,如果父类实例属性是引用类型,可能会出现问题。另外,每次创建子类实例,都要调用 Parent.call(this, name)
,略显繁琐。
ES6 引入了 class
和 extends
关键字,提供了更简洁的语法糖,但本质上仍然是基于原型链的。
class Parent { constructor(name) { this.name = name; } sayHello() { console.log("Hello, I'm " + this.name); } } class Child extends Parent { constructor(name, age) { super(name); // 调用父类的构造函数 this.age = age; } sayAge() { console.log("I'm " + this.age + " years old."); } } const child = new Child("Bob", 12); child.sayHello(); // 输出: Hello, I'm Bob child.sayAge(); // 输出: I'm 12 years old.
使用 class
和 extends
可以使代码更易读,更易维护。super()
关键字用于调用父类的构造函数和方法,避免了手动调用 Parent.call(this, name)
。
还有一些其他的继承方式,例如组合继承、寄生式继承、寄生组合式继承等,但实际应用中,基于原型链的继承和 ES6 的 class
继承是最常用的。选择哪种方式,取决于具体的项目需求和团队规范。 个人更倾向于使用 ES6 的 class
继承,代码更简洁,也更符合现代 JavaScript 的编程风格。
如何避免原型链污染?
原型链污染是一个安全问题,攻击者可以通过修改对象的原型来影响所有基于该原型创建的对象。在继承的场景下,尤其需要注意这个问题。
避免原型链污染的关键在于:
- 避免直接修改
Object.prototype
或其他内置对象的原型。 这是最重要的一点。永远不要为了方便而直接修改内置对象的原型,这会带来很大的安全风险。 - 使用
Object.create(null)
创建对象。 这种方式创建的对象没有原型链,可以避免原型链污染。但需要注意的是,这种对象没有继承任何内置方法,例如toString
、hasOwnProperty
等。 - 使用
Object.freeze()
或Object.seal()
冻结对象。Object.freeze()
可以冻结对象,使其属性不可修改。Object.seal()
可以封闭对象,使其不能添加新的属性,但可以修改已有的属性。 - 使用
Map
或WeakMap
代替普通对象。Map
和WeakMap
不会受到原型链污染的影响。 - 对用户输入进行验证和过滤。 避免用户输入的数据直接用于修改对象的属性。
在继承的场景下,如果需要修改原型,尽量使用 Object.create()
创建一个新的原型对象,而不是直接修改父类的原型。同时,对子类添加的属性进行验证,避免恶意代码注入。
super
关键字在继承中的作用是什么?
super
关键字在 ES6 的 class
继承中扮演着重要的角色,它主要有两个作用:
- 调用父类的构造函数。 在子类的构造函数中,必须先调用
super()
才能使用this
关键字。super()
相当于调用Parent.call(this, ...args)
,用于初始化父类的属性。如果没有调用super()
,会抛出一个ReferenceError
错误。 - 访问父类的方法。 可以使用
super.methodName()
调用父类的方法。这在子类需要重写父类方法,但又想保留父类原有功能时非常有用。
class Parent { constructor(name) { this.name = name; } sayHello() { console.log("Hello, I'm " + this.name); } } class Child extends Parent { constructor(name, age) { super(name); // 调用父类的构造函数 this.age = age; } sayHello() { super.sayHello(); // 调用父类的 sayHello 方法 console.log("I'm also a child."); } } const child = new Child("Charlie", 8); child.sayHello(); // 输出: // Hello, I'm Charlie // I'm also a child.
super
关键字简化了继承的语法,使代码更易读,更易维护。它确保了父类的初始化逻辑能够正确执行,同时也提供了访问父类方法的便捷方式。
如何实现多重继承?
JavaScript 本身并不支持传统意义上的多重继承,即一个类同时继承多个父类的属性和方法。但可以通过一些技巧来模拟多重继承的效果。
- 混入 (Mixins)。 混入是一种将多个类的属性和方法合并到一个类中的技术。可以通过遍历多个类的原型,将它们的属性和方法复制到目标类的原型上。
function mixin(target, ...sources) { for (const source of sources) { for (const key of Object.getOwnPropertyNames(source.prototype)) { if (key !== 'constructor') { Object.defineProperty(target.prototype, key, Object.getOwnPropertyDescriptor(source.prototype, key)); } } } } class CanFly { fly() { console.log("I can fly!"); } } class CanSwim { swim() { console.log("I can swim!"); } } class Duck { constructor(name) { this.name = name; } } mixin(Duck, CanFly, CanSwim); const duck = new Duck("Donald"); duck.fly(); // 输出: I can fly! duck.swim(); // 输出: I can swim!
混入的优点是简单易用,可以灵活地组合多个类的功能。缺点是可能会出现命名冲突,需要仔细处理。
- 组合 (Composition)。 组合是一种将多个对象组合在一起,形成一个新的对象的技术。每个对象负责一部分功能,通过组合将这些功能整合在一起。
class Flyable { constructor(obj) { this.obj = obj; } fly() { console.log(this.obj.name + " can fly!"); } } class Swimmable { constructor(obj) { this.obj = obj; } swim() { console.log(this.obj.name + " can swim!"); } } class Duck { constructor(name) { this.name = name; this.flyable = new Flyable(this); this.swimmable = new Swimmable(this); } fly() { this.flyable.fly(); } swim() { this.swimmable.swim(); } } const duck = new Duck("Daisy"); duck.fly(); // 输出: Daisy can fly! duck.swim(); // 输出: Daisy can swim!
组合的优点是避免了命名冲突,代码更清晰,更易维护。缺点是需要手动将各个对象组合在一起,略显繁琐。
选择哪种方式取决于具体的应用场景。如果需要灵活地组合多个类的功能,可以选择混入。如果需要避免命名冲突,代码更清晰,可以选择组合。个人更倾向于使用组合,因为它更符合面向对象的设计原则。
理论要掌握,实操不能落!以上关于《JS怎么实现类继承?手把手教你轻松搞定类继承》的详细介绍,大家都掌握了吧!如果想要继续提升自己的能力,那么就来关注golang学习网公众号吧!
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
152 收藏
-
236 收藏
-
271 收藏
-
310 收藏
-
150 收藏
-
276 收藏
-
351 收藏
-
231 收藏
-
243 收藏
-
452 收藏
-
115 收藏
-
273 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 508次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 497次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 484次学习