JavaScript 三种继承方式详解:原型链、apply与call+prototype
本文深入解析JavaScript的三种主要继承模式:基于prototype的原型链继承、利用apply方法继承以及结合call与prototype的混合继承。通过代码示例对比各方式在参数传递、属性复制及instanceof类型判断上的表现,指出原型链无法传参、apply导致类型丢失等局限,并论证call+prototype组合在保持类型关系与功能复用上的优势,同时提及更完善的继承方案以解决重复初始化问题。
原型链继承方式
第一种实现方式是基于prototype属性的原型链继承:
//父类
function person(){
this.hair = 'black';
this.eye = 'black';
this.skin = 'yellow';
this.view = function(){
return this.hair + ',' + this.eye + ',' + this.skin;
}
}
//子类
function man(){
this.feature = ['beard','strong'];
}
man.prototype = new person();
var one = new man();
console.log(one.feature); //['beard','strong']
console.log(one.hair); //black
console.log(one.eye); //black
console.log(one.skin); //yellow
console.log(one.view()); //black,black,yellow
该方式实现最为直观,只需将子类的prototype属性指向父类的一个实例即可。完成此赋值后,子类实例便可直接调用父类中定义的方法。
prototype属性的含义是什么?prototype即原型,由function定义的对象默认拥有一个名为prototype的属性,其值是一个对象类型。
该默认属性用于实现属性的链式向上查找机制。具体而言,当访问某个对象的属性不存在时,JavaScript引擎会通过该对象的prototype属性所属对象继续查找该属性。若在此层级仍未找到呢?
JavaScript会自动继续查找prototype属性所指向对象的prototype属性,从而沿着原型链一直向上索引查找,直到找到目标属性或prototype最终为null(即undefined)。
以示例中one.view()方法的调用为例,JavaScript首先在一one实例中查找view()方法,由于未找到,转而查找man.prototype属性。
由于man.prototype的值是person的一个实例,该实例中包含view()方法,因此调用成功。
apply方法继承
第二种实现方式是利用apply方法:
//父类
function person(){
this.hair = 'black';
this.eye = 'black';
this.skin = 'yellow';
this.view = function(){
return this.hair + ',' + this.eye + ',' + this.skin;
}
}
//子类
function man(){
// person.apply(this,new Array());
person.apply(this,[]);
this.feature = ['beard','strong'];
}
var one = new man();
console.log(one.feature); //['beard','strong']
console.log(one.hair); //black
console.log(one.eye); //black
console.log(one.skin); //yellow
console.log(one.view()); //black,black,yellow
注意:如果apply方法的参数为空,即没有传递具体参数,则必须通过new Array()或[]来传递空数组,直接传递null是无效的。
call与prototype结合继承
第三种实现方式是结合call与prototype的方式:
//父类
function person(){
this.hair = 'black';
this.eye = 'black';
this.skin = 'yellow';
this.view = function(){
return this.hair + ',' + this.eye + ',' + this.skin;
}
}
//子类
function man(){
// person.apply(this,new Array());
person.call(this,[]);
this.feature = ['beard','strong'];
}
man.prototype = new person();
var one = new man();
console.log(one.feature); //['beard','strong']
console.log(one.hair); //black
console.log(one.eye); //black
console.log(one.skin); //yellow
console.log(one.view()); //black,black,yellow
采用call方式实现继承时,多出一行代码man.prototype = new person();。原因在于,call方法仅实现了方法的借用替换,并未执行对象属性的复制操作。Google Map API的继承机制便采用了这种方式。
上述内容总结了三种继承方式的实现逻辑,但每种方法均存在各自的优缺点。
带参继承的实现与局限
假设父类定义如下:
//父类
function person(hair,eye,skin){
this.hair = hair;
this.eye = eye;
this.skin = skin;
this.view = function(){
return this.hair + ',' + this.eye + ',' + this.skin;
}
}
若子类在创建对象时需要向父类person传递参数,则prototype继承方式不再适用,必须采用apply或call方式:
//apply方式
//子类
function man(hair,eye,skin){
person.apply(this,[hair,eye,skin]);
this.feature = ['beard','strong'];
}
//call方式
//子类
function man(hair,eye,skin){
person.call(this,hair,eye,skin);
this.feature = ['beard','strong'];
}
然而,使用apply方法仍存在缺陷。原因在于JavaScript中有一个重要运算符instanceof,用于判断某个对象是否属于某种类型。
对于该示例,one实例除了是man类型外,理论上也应属于person类型。但在apply方式继承后,one并不属于person类型,即(one instanceof person)的值为false。
综合上述分析,最佳的继承方式是call结合prototype的方式。随后你可以验证(one instanceof BaseClass)的值是否为true。
更完善的继承方案
第三种继承方式(call+prototype)也存在缺陷:子类new对象时需传递一遍父类所需的参数,且会重新执行父类中的属性和方法初始化。下面介绍的继承方式才是更完善的方案:
function Person(name){
this.name = name;
}
Person.prototype.getName = function() {
return this.name;
}
function Chinese(name, nation) {
Person.call(this, name);
this.nation = nation;
}
//继承方法
function inherit(subClass, superClass) {
function F() {}
F.prototype = superClass.prototype;
subClass.prototype = new F();
subClass.prototype.constructor = subClass.constructor;
}
inherit(Chinese, Person);
Chinese.prototype.getNation = function() {
return this.nation;
};
var p = new Person('shijun');
var c = new Chinese("liyatang", "China");
console.log(p); // Person {name: "shijun", getName: function}
console.log(c); // Chinese {name: "liyatang", nation: "China", constructor: function, getNation: function, getName: function}
console.log(p.constructor); // function Person(name){}
console.log(c.constructor); // function Chinese(){}
console.log(c instanceof Chinese); // true
console.log(c instanceof Person); // true
以上关于JavaScript三种继承方式及其优缺点的探讨即为全部内容,希望能为大家提供参考,也希望大家多多支持本站。
JS读取本地文件时报错或无响应的常见原因
2026-11-05 16:32:31
let、const与var的区别:JavaScript变量声明最佳实践
2026-11-05 14:16:38
JS中直接定义静态二维数组的字面量写法
2026-11-05 14:16:08
JS 怎么查找字符串中所有匹配项的位置
2026-11-05 14:15:53
JavaScript新手常犯的编码错误与最佳实践指南
2026-11-04 22:12:38
