1. 原型链继承
通过将一个对象的 prototype 属性指向另一个对象,从而实现继承。
function Parent() {
this.name = "Parent";
}
function Child() {}
// 设置原型链
Child.prototype = new Parent();
const child = new Child();
console.log(child.name); // "Parent"
子类通过原型链继承父类的属性和方法。父类的属性会成为子类实例的共享属性。
function Parent() {
this.hobbies = ["reading", "coding"]; // 引用类型属性
}
function Child() {}
// 设置原型链
Child.prototype = new Parent();
const child1 = new Child();
const child2 = new Child();
child1.hobbies.push("traveling"); // 修改 child1 的 hobbies
console.log(child1.hobbies); // ["reading", "coding", "traveling"]
console.log(child2.hobbies); // ["reading", "coding", "traveling"] -> 被意外修改
当使用原型链继承时,父类的引用类型属性会被所有子类实例共享,因此修改一个实例的属性会影响所有其他实例。
如何解决这个问题?通过在子类的构造函数中调用父类构造函数,避免共享引用类型属性,也就是下面所提到的借用构造函数的方式。
2. 借用构造函数
通过在子类构造函数中调用父类的构造函数实现。
function Parent(name) {
this.name = name;
this.hobbies = ["reading", "coding"];
}
function Child(name, age) {
Parent.call(this, name); // 借用 Parent 的构造函数
this.age = age;
}
const child1 = new Child("Alice", 25);
const child2 = new Child("Bob", 30);
child1.hobbies.push("traveling");
console.log(child1.hobbies); // ["reading", "coding", "traveling"]
console.log(child2.hobbies); // ["reading", "coding"]
父类构造函数中定义的方法会在每个子类实例中重新创建一份,导致方法无法复用,占用更多内存。
3. 组合继承(原型链 + 构造函数)
结合原型链和构造函数继承的优点。
function Parent(name) {
this.name = name;
this.hobbies = ["reading", "coding"];
}
Parent.prototype.greet = function () {
console.log(`Hello, ${this.name}`);
};
function Child(name, age) {
Parent.call(this, name); // 借用构造函数
this.age = age;
}
Child.prototype = new Parent(); // 设置原型链
Child.prototype.constructor = Child; // 修复构造函数引用
const child1 = new Child("Alice", 25);
child1.greet(); // "Hello, Alice"
继承了父类的实例属性和原型方法,不会共享引用类型属性,每个实例都可以单独使用父类的方法。
问题:父类构造函数被调用两次:一次在原型链设置时,一次在子类构造函数中。
4. 寄生式组合继承
优化组合继承,避免调用父类构造函数两次。
function Parent(name) {
this.name = name;
this.hobbies = ["reading", "coding"];
}
Parent.prototype.greet = function () {
console.log(`Hello, ${this.name}`);
};
function Child(name, age) {
Parent.call(this, name);
this.age = age;
}
// 创建子类的原型对象,避免调用父类构造函数
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
const child1 = new Child("Alice", 25);
child1.greet(); // "Hello, Alice"
Object.create()以一个现有对象作为原型,创建一个新对象。
采用这种方式父类构造函数只调用一次,继承了父类的实例属性和原型方法,高效且是目前最常用的继承方式。
5. Object.create 原型继承
通过 Object.create 方法创建新对象,直接设置其原型。
const parent = {
greet: function () {
console.log(`Hello, ${this.name}`);
},
};
const child = Object.create(parent);
child.name = "Alice";
child.greet(); // "Hello, Alice"
简单、轻量,适合只需要继承原型对象的方法和属性的场景。
问题:无法向父类传递参数
6. extends 继承
在ES6之后,可以使用extends 关键字进行继承。
class Parent {
constructor(name) {
this.name = name;
}
greet() {
console.log(`Hello, ${this.name}`);
}
}
class Child extends Parent {
constructor(name, age) {
super(name); // 调用父类的构造函数
this.age = age;
}
}
const child = new Child("Alice", 25);
child.greet(); // "Hello, Alice"
简化了继承的语法,支持调用父类构造函数(super),更符合面向对象编程的习惯。
983

被折叠的 条评论
为什么被折叠?



