JavaScript 继承机制详解:从原型链到寄生组合式
本文深入解析 JavaScript 中的继承机制。从 Object 原型基础出发,详细阐述原型链继承、构造函数继承、组合继承、拷贝继承及寄生组合继承等核心模式。通过代码示例对比各方式优劣,重点讲解如何避免原型链继承的属性共享问题及构造函数继承的方法复用缺陷,帮助开发者掌握 ES5 及之前版本的经典继承方案。
JavaScript 继承基础与 Object 原型
JavaScript 语言本身并未直接提供传统面向对象语言中的继承语法,但开发者可以通过构造函数与原型链等机制实现类似的功能。
var o=new Object();
在 JavaScript 中,通过构造函数实例化对象时,该对象实际上已经继承了 Object 的所有属性与方法。之所以能访问这些方法,是因为它们被定义在原型对象中,而非构造函数本身。
console.log(o.__proto__ === Object.prototype) // true 继承的本质
console.log(o.__proto__ === Object);
console.log(Object.__proto__ === Function.prototype);
console.log(Function.prototype.__proto__ === Object.prototype);
console.log(Number.__proto__ === Function.prototype);
Object 被视为所有对象的祖先,这体现了“万物皆对象”的设计哲学。
JavaScript 中的所有内置对象均继承自 Object。
var myNumber = new Number(10); //实例化一个数字对象
字符串字面量在底层被视为 String 对象的实例。
var s = ‘str';
通过这种机制,对象不仅能访问自身定义的属性与方法,还能访问其父类(原型链上游)的属性与方法。
console.log(s.toUpperCase());
console.log(s.toString());
自定义对象的原型链继承
对于自定义对象,开发者需要手动建立继承关系。
// 父类
var Person = function(){
this.name='AV老师';
this.test='测试中的毕福剑';
}
Person.prototype={
say:function(){
console.log('呀买碟');
}
}
// 构造函数
var Canglaoshi = function(name,age,cup){
this.name=name;
this.age=age;
this.cup=cup;
}
// 继承person,则拥有person原型中的方法
Canglaoshi.prototype=new Person();
Canglaoshi.prototype.ppp=function(){
console.log('啪啪啪');
}
// 苍老师拥有了person中的方法
var xiaocang=new Canglaoshi('空空',18,'E');
xiaocang.say();
console.log(xiaocang.name);
console.log(xiaocang.age);
console.log(xiaocang.cup);
console.log(xiaocang.test);
下面通过具体代码演示自定义对象如何构建原型链。
(function() {
//父类
function SuperParent() {
this.name = 'mike';
}
//子类继承父亲 - 二次继承:
function Parent() {
this.age = 12;
}
Parent.prototype = new SuperParent(); //通过原型,形成链条
var parent = new Parent();
console.log("测试父亲可以访问爷爷属性:" + parent.name);
console.log("测试父亲可以访问自己的属性:" + parent.age);
//继续原型链继承 - 三次继承
function Child() { //brother构造
this.weight = 60;
}
Child.prototype = new Parent(); //继续原型链继承
//原型链测试2
//儿子集成爷爷
var child = new Child();
console.log("测试儿子可以访问爷爷的属性:" + child.name); //继承了Parent和Child,弹出mike
console.log("测试儿子可以访问父亲的属性:" + child.age); //弹出12
console.log("测试儿子可以访问自己独特的属性:" + child.weight); //弹出12
//原型链测试
//爷爷可以访问Object中的方法
var test = new SuperParent();
console.log(test.name);
console.log(test.toString());
//访问链: SuperParent构造对象--》SuperParent原型对象--》Object对象--》Obect原型对象(找到toString方法)--》null
console.log(child.name);
//原型链:首先访问Child构造函数,发现没有name属性--》寻找__proto__,判断起指针是否为空--》指向Child原型函数,寻找有没有name属性--》
//---》没有找到,则判断其__proto__属性是否为null,如果不为null,继续搜索--》找到parent对象,检查是否有name属性,没有。。。。
})()
构造函数与组合继承模式
构造函数继承(Call/Apply 继承)是另一种常见方式。
(function() {
function People() {
this.race = '人类';
}
People.prototype = {
eat: function() {
alert('吃吃吃');
}
}
/*人妖对象*/
function Shemale(name, skin) {
People.apply(this, arguments); // 用call也是一样的,注意第二个参数
this.name = name;
this.skin = skin;
}
//实例化
var zhangsan = new Shemale('张三', '黄皮肤')
console.log(zhangsan.name); //张三
console.log(zhangsan.skin); //黄皮肤
console.log(zhangsan.race); //人类
})()
组合继承结合了原型链与构造函数继承的优点,是 ES5 中最常用的继承模式之一。
(function() {
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.say = function() {}
function Man(name, age, no) {
/*会自动调用Person的方法,同时将name age传递过去*/
Person.call(this, name, age);
//自己的属性
this.no = no;
}
Man.prototype = new Person();
var man = new Man("张三", 11, "0001");
console.log(man.name);
console.log(man.age);
console.log(man.no);
})()
拷贝与寄生组合继承
拷贝继承通过将父类属性逐个复制到子类实例中来实现继承。
<script>
+(function() {
var Chinese = {
nation: '中国'
};
var Doctor = {
career: '医生'
};
// 请问怎样才能让"医生"去继承"中国人",也就是说,我怎样才能生成一个"中国医生"的对象?
// 这里要注意,这两个对象都是普通对象,不是构造函数,无法使用构造函数方法实现"继承"。
function extend(p) {
var c = {};
for (var i in p) {
c[i] = p[i];
}
c.uber = p;
return c;
}
var Doctor = extend(Chinese);
Doctor.career = '医生';
alert(Doctor.nation); // 中国
})()
</script>
寄生组合继承旨在解决组合继承中父类构造函数被调用两次的问题,是更高效的继承方案。
<script>
+(function() {
/*继承的固定函数*/
function inheritPrototype(subType, superType) {
var prototype = Object(superType.prototype);
prototype.constructor = subType;
subType.prototype = prototype;
}
function Person(name) {
this.name = name;
}
Person.prototype.say = function() {}
function Student(name, age) {
Person.call(this, name);
this.age = age;
}
inheritPrototype(Student, Person);
var xiaozhang = new Student('小张', 20);
console.log(xiaozhang.name)
})()
</script>
此外,还可以借助第三方框架或库来简化继承的实现过程。
<script src='simple.js'></script>
<!-- 使用的第三方框架simple.js -->
<script>
+(function() { < script >
var Person = Class.extend({
init: function(age, name) {
this.age = age;
this.name = name;
},
ppp: function() {
alert("你懂的");
}
});
var Man = Person.extend({
init: function(age, name, height) {
this._super(age, name);
this.height = height;
},
ppp: function() {
/*调用父类的同名方法*/
this._super();
/*同时又可以调用自己的方法*/
alert("好害羞 -,- ");
}
});
var xiaozhang = new Man(21, '小张', '121');
xiaozhang.ppp();
})()
希望上述内容能帮助读者更好地理解 JavaScript 程序设计中的继承原理。
JavaScript异步发送HTTP请求时怎么优雅地处理错误
2026-11-05 17:10:44
5款主流JS在线运行工具对比:CodePen、JSFiddle与StackBlitz怎么选
2026-11-05 17:04:04
JavaScript入门书籍避坑:这些书不适合初学者
2026-11-05 16:31:15
JS使用Math.round实现保留两位小数的原理与代码
2026-11-05 15:35:55
JavaScript 原生 Date 对象怎么实现日期加减
2026-11-05 14:30:00
