JavaScript实现继承的三种方式:借用构造函数、拷贝继承与原型继承
本文详解JavaScript中实现继承的三种核心方法:借用构造函数、原型拷贝继承及原型继承。通过对比分析,阐述如何利用call方法实现属性继承,使用for-in遍历实现方法拷贝,以及通过修改原型链指向实现对象复用。文章结合具体代码案例,总结各方法的优缺点及适用场景,帮助开发者掌握JS面向对象编程中的继承机制。
继承
1. 什么是继承
在面向对象编程中,继承描述了类与类之间的层级关系。尽管JavaScript语言本身没有传统的类(class)概念,但开发者可以通过构造函数来模拟类的行为,并借助原型机制来实现继承逻辑。
- 继承的核心目的在于实现数据共享。在JavaScript中,通过继承机制,子类可以复用父类的属性和方法,从而避免代码冗余,实现数据的有效共享。
理解继承的关键在于原型(Prototype)的概念,它主要承担两项核心职责:
- 第一项职责是实现数据共享,通过原型链机制,多个实例可以共享同一份属性和方法,从而显著节省内存空间。
- 第二项职责是实现继承,允许子类对象访问父类原型上的属性和方法,从而建立对象间的层级关联。
从关系层面看,继承定义了父类与子类之间的层级结构,明确了对象间的归属与派生关系。
为了更直观地理解,我们可以观察以下具体场景:
以“人类”为基准,具备姓名、性别、年龄等属性,以及吃饭、睡觉等行为方法。
“学生”作为人类的一种,除了继承姓名、性别、年龄及吃饭、睡觉等行为外,还具备特有的学习行为。
“老师”同样继承人类的基本属性(姓名、性别、年龄)和行为(吃饭、睡觉),但额外拥有工资属性和教学行为。
“程序员”作为特定职业,继承人类基础属性与行为,并增加工资属性及敲代码这一特定行为。
“司机”类别继承人类的基础属性(姓名、性别、年龄)和行为(吃饭、睡觉),并拥有工资属性及开车行为。
若将视角转向“动物”类别,其基础属性包括体重和颜色,基本行为为吃。
“狗”作为动物的一种,继承体重、颜色和吃行为,并具备咬人这一特有行为。
“二哈”作为狗的一个子类,不仅继承体重、颜色、吃和咬人行为,还额外具备逗主人开心、发出“汪汪”声以及说“你好帅”等特有行为。
面向对象编程的三大核心特性包括:封装、继承和多态。
封装是指将数据或功能打包在一起的过程。具体表现为:将单个值存储于变量中、将重复代码封装为函数、将相关属性整合至对象,或将功能相似的方法归入同一对象,甚至将相似对象存放于同一JavaScript文件中,均属于封装的范畴。
多态指同一对象在不同情境下表现出的不同行为,或同一行为针对不同对象产生不同结果。实现多态的前提是存在继承关系。虽然JavaScript可以模拟多态,但在实际开发中较少直接使用或刻意模拟这一特性。
2. JavaScript继承的三种方法
第一种方法是构造函数的属性继承,通常称为“借用构造函数”。
在使用该方法时,无需修改子类的原型指向,而是直接调用父级的构造函数,将父级构造函数的执行上下文绑定到子类实例上,从而完成属性的赋值与继承。
简而言之,“借用构造函数”即是将父级构造函数提取出来,在当前子类的上下文中执行一次即可。
具体实现方式为“借用构造函数”模式:
调用语法为:构造函数名.call(当前实例对象, 属性参数1, 属性参数2, ...)。
该方法的主要优点是成功解决了属性继承问题,并确保每个实例拥有独立的属性值,避免了引用类型的共享问题。
然而,其缺陷在于无法继承父级原型(prototype)中定义的方法,导致方法无法复用。
function Person (name, age) {
this.type = 'human'
this.name = name
this.age = age
}
function Student (name, age) {
// 借用构造函数继承属性成员
Person.call(this, name, age)
}
var s1 = Student('张三', 18)
console.log(s1.type, s1.name, s1.age) // => human 张三 18
以下是借用构造函数的具体案例:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>title</title>
<script>
function Person(name, age, sex, weight) {
this.name = name;
this.age = age;
this.sex = sex;
this.weight = weight;
}
Person.prototype.sayHi = function () {
console.log("您好");
};
function Student(name,age,sex,weight,score) {
//借用构造函数
Person.call(this,name,age,sex,weight);
this.score = score;
}
var stu1 = new Student("小明",10,"男","10kg","100");
console.log(stu1.name, stu1.age, stu1.sex, stu1.weight, stu1.score);
var stu2 = new Student("小红",20,"女","20kg","120");
console.log(stu2.name, stu2.age, stu2.sex, stu2.weight, stu2.score);
var stu3 = new Student("小丽",30,"妖","30kg","130");
console.log(stu3.name, stu3.age, stu3.sex, stu3.weight, stu3.score);
</script>
</head>
<body>
</body>
</html>
第二种方法是构造函数的原型方法继承,即“拷贝继承”(基于for-in循环)。
拷贝继承的核心逻辑是将源对象(通常是父类原型)中的属性或方法,直接遍历并复制到目标对象(子类原型)中。
function Person (name, age) {
this.type = 'human'
this.name = name
this.age = age
}
Person.prototype.sayName = function () {
console.log('hello ' + this.name)
}
function Student (name, age) {
Person.call(this, name, age)
}
// 原型对象拷贝继承原型对象成员
for(var key in Person.prototype) {
Student.prototype[key] = Person.prototype[key]
}
var s1 = Student('张三', 18)
s1.sayName() // => hello 张三
以下是拷贝继承的具体案例:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>title</title>
<script>
//拷贝继承;把一个对象中的属性或者方法直接复制到另一个对象中
function Person() {
}
Person.prototype.age=10;
Person.prototype.sex="男";
Person.prototype.height=100;
Person.prototype.play=function () {
console.log("玩的好开心");
};
var obj2={};
//Person的构造中有原型prototype,prototype就是一个对象,那么里面,age,sex,height,play都是该对象中的属性或者方法
for(var key in Person.prototype){
obj2[key]=Person.prototype[key];
}
console.dir(obj2);
obj2.play();
</script>
</head>
<body>
</body>
</html>
第三种继承方式是“原型继承”。
原型继承的关键操作是改变子类的原型指向,使其指向父类的实例,从而建立原型链连接。
function Person (name, age) {
this.type = 'human'
this.name = name
this.age = age
}
Person.prototype.sayName = function () {
console.log('hello ' + this.name)
}
function Student (name, age) {
Person.call(this, name, age)
}
// 利用原型的特性实现继承
Student.prototype = new Person()
var s1 = Student('张三', 18)
console.log(s1.type) // => human
s1.sayName() // => hello 张三
第三种综合方案是“组合继承”,它结合了原型继承与借用构造函数继承的优点。

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>title</title>
<script>
//原型实现继承
//借用构造函数实现继承
//组合继承:原型继承+借用构造函数继承
function Person(name,age,sex) {
this.name=name;
this.age=age;
this.sex=sex;
}
Person.prototype.sayHi=function () {
console.log("萨瓦迪卡");
};
function Student(name,age,sex,score) {
//借用构造函数:属性值重复的问题
Person.call(this,name,age,sex);
this.score=score;
}
//改变原型指向----继承
Student.prototype=new Person();//不传值
Student.prototype.eat=function () {
console.log("吃东西");
};
var stu=new Student("小黑",20,"男","100分");
console.log(stu.name,stu.age,stu.sex,stu.score);
stu.sayHi();
stu.eat();
var stu2=new Student("小黑黑",200,"男人","1010分");
console.log(stu2.name,stu2.age,stu2.sex,stu2.score);
stu2.sayHi();
stu2.eat();
//属性和方法都被继承了
</script>
</head>
<body>
</body>
</html>
继承方式小结:
原型继承:通过修改原型指向,使子类实例能够访问父类原型上的方法。
借用构造函数继承:主要用于解决实例属性的继承问题,确保属性独立。
组合继承:融合原型继承与借用构造函数继承两种技术。
组合继承的优势在于,它既能解决属性继承导致的值共享问题,又能通过原型链解决方法继承的问题,实现了属性与方法的完整复用。
拷贝继承:通过遍历方式,将对象中需要共享的属性或方法直接复制到另一个对象中,实现简单的属性与方法复用。
总结
至此,关于JavaScript继承的三种方法(借用构造函数、拷贝继承、原型继承)的介绍已完成。如需了解更多JavaScript继承相关内容,建议查阅本站过往文章或继续浏览后续系列文章,感谢大家的支持!
JS语法错误会阻止加载吗?区分SyntaxError与资源加载失败
2026-11-05 16:08:46
JavaScript常见SyntaxError语法错误排查与修复方法
2026-11-05 13:58:44
JS中slice、substring和substr截取字符串的区别
2026-11-05 13:43:47
JS 转 TS 手动迁移最佳实践:从配置 tsconfig 到消除 any 类型
2026-11-05 11:53:32
为什么JS数组push后得到的是数字而不是数组?
2026-11-05 09:59:18
