实现ES6的extends继承
手写实现ES6 class继承机制,理解JavaScript原型链和构造函数的继承原理
问题
ES6引入了class和extends关键字,使得JavaScript的面向对象编程更加直观。但在底层,它仍然是基于原型链实现的。这道题要求我们手动实现extends的继承机制,理解子类如何继承父类的属性和方法。
解答
/**
* 实现ES6的extends继承
* @param {Function} Child - 子类构造函数
* @param {Function} Parent - 父类构造函数
*/
function myExtends(Child, Parent) {
// 1. 创建一个空函数作为中介,避免直接实例化父类
function F() {}
// 2. 将空函数的原型指向父类原型
F.prototype = Parent.prototype;
// 3. 将子类的原型指向空函数的实例
// 这样子类原型就能访问父类原型上的方法,同时不会影响父类
Child.prototype = new F();
// 4. 修正子类原型的constructor指向
Child.prototype.constructor = Child;
// 5. 保存父类引用,方便子类调用父类方法(类似super)
Child.__proto__ = Parent;
// 6. 为子类添加一个静态属性,指向父类原型
Child.super = Parent.prototype;
}
// 更简洁的实现方式(使用Object.create)
function myExtends2(Child, Parent) {
// 使用Object.create创建一个以Parent.prototype为原型的对象
Child.prototype = Object.create(Parent.prototype);
// 修正constructor指向
Child.prototype.constructor = Child;
// 继承父类的静态属性和方法
Object.setPrototypeOf(Child, Parent);
}
使用示例
// 定义父类
function Animal(name) {
this.name = name;
this.energy = 100;
}
Animal.prototype.eat = function() {
console.log(`${this.name} is eating`);
this.energy += 10;
};
Animal.prototype.sleep = function() {
console.log(`${this.name} is sleeping`);
};
// 父类静态方法
Animal.getType = function() {
return 'Animal';
};
// 定义子类
function Dog(name, breed) {
// 调用父类构造函数,继承实例属性
Animal.call(this, name);
this.breed = breed;
}
// 使用myExtends实现继承
myExtends2(Dog, Animal);
// 子类添加自己的方法
Dog.prototype.bark = function() {
console.log(`${this.name} is barking: Woof!`);
};
// 子类重写父类方法
Dog.prototype.eat = function() {
console.log(`${this.name} the dog is eating dog food`);
this.energy += 15;
};
// 测试
const myDog = new Dog('Buddy', 'Golden Retriever');
console.log(myDog.name); // Buddy
console.log(myDog.breed); // Golden Retriever
console.log(myDog.energy); // 100
myDog.eat(); // Buddy the dog is eating dog food
console.log(myDog.energy); // 115
myDog.sleep(); // Buddy is sleeping
myDog.bark(); // Buddy is barking: Woof!
// 验证原型链
console.log(myDog instanceof Dog); // true
console.log(myDog instanceof Animal); // true
console.log(myDog.constructor === Dog); // true
// 验证静态方法继承
console.log(Dog.getType()); // Animal
关键点
-
原型链继承:通过设置
Child.prototype为一个继承自Parent.prototype的对象,实现方法的继承 -
构造函数继承:在子类构造函数中使用
Parent.call(this, ...args)调用父类构造函数,继承实例属性 -
使用中间对象:通过空函数
F或Object.create()创建中间对象,避免直接实例化父类,防止父类构造函数的副作用 -
修正constructor:继承后需要手动修正
Child.prototype.constructor,使其指向子类本身 -
静态属性继承:通过
Object.setPrototypeOf(Child, Parent)或Child.__proto__ = Parent实现静态方法的继承 -
组合继承:结合原型链继承和构造函数继承,这是最常用的继承模式,也是ES6 extends的底层实现原理
-
instanceof验证:正确实现后,子类实例应该同时是子类和父类的实例
目录