原生JavaScript实现继承方式及其优缺点详解

作者:小括号

时间:2026-10-24

浏览:475

本文详细解析JavaScript中原生实现的五种继承方式,包括原型继承、构造函数继承、组合式继承及寄生式组合继承。通过代码示例对比各方式的优缺点,重点阐述如何解决属性共享、参数传递及原型链问题,并指出寄生式组合继承为最常用方案。

文章目录

前言

近期在复习JavaScript的基础知识,为接下来的开发工作做准备,因此整理并记录相关学习心得。

本文主要探讨JavaScript中原生实现的继承机制。

直接通过代码示例进行说明。

首先定义父类代码。

此处创建一个名为Person的类作为父类,其构造函数接收name和age两个参数。

随后在Person的原型上添加一个sayHi方法。


//父类
function Person (name, age) {
    this.name = name || 'no name';
    this.age = age || 0;
}

Person.prototype.sayHi = function () {
    console.log('Hi, I\'m ' + this.name + ' and i\'m ' + this.age + ' years old!');
}

var p = new Person('A',20);
p.sayHi();//Hi, I'm A and i'm 20 years old!

原型继承


//原型继承
function Teacher(){
}

Teacher.prototype=new Person('B',22);
Teacher.prototype.constructor=Teacher;

var t = new Teacher();
t.sayHi();//Hi, I'm B and i'm 22 years old!
console.log(t instanceof Person);//true
console.log(t instanceof Teacher);//true

优点

从上述代码可见,Teacher的实例继承了Person的属性和方法。实例对象既是Person的实例,也是Teacher的实例。此外,这种继承方式的实现逻辑非常简洁。

缺点

观察代码可以发现,Teacher类的name和age属性被固定为B和22,这意味着无法灵活地向父类构造函数传递参数。此外,一个子类只能指定一个原型,因此不支持多继承。参考下方代码:


var t1 = new Teacher();
var t2 = new Teacher();
Teacher.prototype.name = "C";
t1.sayHi();//Hi, I'm C and i'm 22 years old!
t2.sayHi();//Hi, I'm C and i'm 22 years old!

上述代码显示,当原型中的属性或方法被修改时,所有子类实例的属性及方法均会随之改变。这揭示了原型继承的另一个缺陷:所有子类共享同一个原型对象。

此处涉及原型概念,此前曾撰写过相关随笔,但理解尚显模糊,后续将重新撰写关于原型的文章(完成后会附上链接)。

构造函数继承


//构造函数继承
function Teacher (name, age) {
    Person.call(this, name, age);
}

var t1 = new Teacher('B', 22);
var t2 = new Teacher('C', 30);
console.log(t1.name);//B
console.log(t2.name);//C
console.log(t1 instanceof Person);//false
console.log(t1 instanceof Teacher);//true
t1.sayHi();//TypeError: t1.sayHi is not a function
t2.sayHi();//TypeError: t1.sayHi is not a function

优点

与原型继承相比,构造函数继承解决了子类实例共享统一原型的问题,支持向父类构造函数传递参数。此外,可以在子类构造函数中调用多个父类构造函数,实现所谓的多继承(注:此处多继承指通过call或apply调用父类构造函数以获取属性和方法,但由于JavaScript函数对象仅存在一个prototype,因此无法通过原型链体现多继承)。

缺点

从上述代码可见,创建的实例仅为子类的实例,而非父类的实例,无法直观体现继承关系。此外,该方式无法继承父类原型上的属性和方法。

组合式继承


//组合式继承
function Teacher (name, age) {
    Person.call(this, name, age);
}

Teacher.prototype = new Person();
Teacher.prototype.constructor = Teacher;


var t1 = new Teacher('B', 22);
var t2 = new Teacher('C', 30);
Teacher.prototype.name = "D";
console.log(t1.name);//B
console.log(t2.name);//C
t1.sayHi();//Hi, I'm B and i'm 22 years old!
t2.sayHi();//Hi, I'm C and i'm 30 years old!
console.log(t1 instanceof Person);//true
console.log(t1 instanceof Teacher);//true

组合式继承结合了原型继承和构造函数继承的优点,解决了前两种方式存在的部分缺陷。然而,每次创建子类实例时都会创建一个父类实例,尽管这些父类实例内存地址不同,但属性和方法完全一致。因此,通过下文所述的寄生式组合继承方式对其进行完善,以避免不必要的实例构造。

寄生式组合继承


//寄生式组合继承
function Teacher (name, age) {
    Person.call(this, name, age);
}

Teacher.prototype = Object.create(Person.prototype);
Teacher.prototype.constructor = Teacher;


var t1 = new Teacher('B', 22);
var t2 = new Teacher('C', 30);
Teacher.prototype.name = "D";
console.log(t1.name);//B
console.log(t2.name);//C
t1.sayHi();//Hi, I'm B and i'm 22 years old!  
t2.sayHi();//Hi, I'm C and i'm 30 years old!
console.log(t1 instanceof Person);//true
console.log(t1 instanceof Teacher);//true 

上述方式解决了每次创建子类实例时都创建父类实例的问题,这也是JavaScript中最常用的继承方式。若使用Babel将ES6中的class继承转换为ES5代码,会发现其底层实现即为寄生式组合继承。

总结

至此,关于原生JavaScript实现继承方式及其优缺点的介绍到此结束。如需了解更多相关内容,请搜索本站历史文章或继续浏览相关文章,感谢大家的支持!

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章