您现在的位置是:网站首页 > 继承实现方式文章详情
继承实现方式
陈川
【
JavaScript
】
36216人已围观
4452字
继承的概念
继承是面向对象编程的核心概念之一,允许一个对象基于另一个对象来创建。在JavaScript中,继承主要通过原型链实现,ES6之后也引入了基于class的语法糖。理解继承的实现方式对编写可维护、可扩展的代码至关重要。
原型链继承
原型链继承是最基础的继承方式,通过将子类的原型指向父类的实例来实现:
function Parent() {
this.name = 'parent';
this.colors = ['red', 'blue'];
}
Parent.prototype.sayName = function() {
console.log(this.name);
};
function Child() {
this.childProperty = 'child';
}
// 关键步骤:将Child的原型指向Parent的实例
Child.prototype = new Parent();
const child1 = new Child();
child1.sayName(); // 输出: parent
这种方式的缺点是:
- 所有子类实例共享父类实例的引用属性
- 无法向父类构造函数传参
构造函数继承
通过在子类构造函数中调用父类构造函数来解决原型链继承的问题:
function Parent(name) {
this.name = name;
this.colors = ['red', 'blue'];
}
function Child(name) {
Parent.call(this, name); // 关键调用
this.age = 10;
}
const child1 = new Child('child1');
child1.colors.push('green');
const child2 = new Child('child2');
console.log(child2.colors); // ['red', 'blue'] 不共享colors
优点是可以向父类传参且不共享引用属性,但缺点是父类原型上的方法不可访问。
组合继承
组合原型链继承和构造函数继承的优点:
function Parent(name) {
this.name = name;
this.colors = ['red', 'blue'];
}
Parent.prototype.sayName = function() {
console.log(this.name);
};
function Child(name, age) {
Parent.call(this, name); // 第二次调用Parent
this.age = age;
}
Child.prototype = new Parent(); // 第一次调用Parent
Child.prototype.constructor = Child;
const child = new Child('Tom', 10);
child.sayName(); // Tom
这种方式虽然常用,但会调用两次父类构造函数,导致子类原型上有多余的属性。
原型式继承
基于已有对象创建新对象,类似Object.create:
function createObject(o) {
function F() {}
F.prototype = o;
return new F();
}
const parent = {
name: 'parent',
colors: ['red', 'blue']
};
const child = createObject(parent);
console.log(child.name); // parent
适用于不需要单独构造函数的场景,但同样存在引用属性共享的问题。
寄生式继承
在原型式继承基础上增强对象:
function createAnother(original) {
const clone = Object.create(original);
clone.sayHi = function() {
console.log('hi');
};
return clone;
}
const parent = {
name: 'parent'
};
const child = createAnother(parent);
child.sayHi(); // hi
这种方式适合主要关注对象而非自定义类型和构造函数的场景。
寄生组合式继承
目前最理想的继承方式,解决了组合继承的缺点:
function inheritPrototype(child, parent) {
const prototype = Object.create(parent.prototype);
prototype.constructor = child;
child.prototype = prototype;
}
function Parent(name) {
this.name = name;
this.colors = ['red', 'blue'];
}
Parent.prototype.sayName = function() {
console.log(this.name);
};
function Child(name, age) {
Parent.call(this, name);
this.age = age;
}
inheritPrototype(Child, Parent);
const child = new Child('Tom', 10);
child.sayName(); // Tom
这种方式只调用一次父类构造函数,保持原型链不变,是ES6 class继承的底层实现。
ES6 class继承
ES6引入了class语法糖,使继承更直观:
class Parent {
constructor(name) {
this.name = name;
this.colors = ['red', 'blue'];
}
sayName() {
console.log(this.name);
}
}
class Child extends Parent {
constructor(name, age) {
super(name); // 必须调用super
this.age = age;
}
sayAge() {
console.log(this.age);
}
}
const child = new Child('Tom', 10);
child.sayName(); // Tom
class继承底层仍然是基于原型链,但语法更清晰。super关键字用于调用父类构造函数和方法。
多重继承的实现
JavaScript本身不支持多重继承,但可以通过混入(mixin)模式模拟:
class A {
methodA() {
console.log('A');
}
}
class B {
methodB() {
console.log('B');
}
}
function mixin(...classes) {
return classes.reduce((base, mixin) => {
Object.getOwnPropertyNames(mixin.prototype)
.filter(name => name !== 'constructor')
.forEach(name => {
base.prototype[name] = mixin.prototype[name];
});
return base;
}, class {});
}
class C extends mixin(A, B) {
methodC() {
console.log('C');
}
}
const c = new C();
c.methodA(); // A
c.methodB(); // B
继承与性能考虑
原型链查找会影响性能,过深的原型链会导致查找时间增加。现代JavaScript引擎优化了原型查找,但仍需注意:
// 不推荐的深层继承链
function A() {}
function B() { A.call(this); }
function C() { B.call(this); }
function D() { C.call(this); }
// ...更多层级
// 更扁平的结构通常性能更好
class Base {
commonMethod() {}
}
class Feature1 extends Base {
method1() {}
}
class Feature2 extends Base {
method2() {}
}
继承与组合的对比
在复杂场景下,组合可能比继承更灵活:
// 组合示例
const canEat = {
eat() {
console.log('Eating');
}
};
const canWalk = {
walk() {
console.log('Walking');
}
};
function Person() {
// 组合多个行为
Object.assign(this, canEat, canWalk);
}
const person = new Person();
person.eat();
person.walk();
组合模式避免了继承的层级问题,更易于维护和扩展。
上一篇: constructor属性
下一篇: 对象拷贝与比较