JS的强大继承关系,从JS继承与原型链剖析
2023-12-03 23:29:52
最近在排查巩固面试知识点的时候,发现继承,原型链这一块真是一生之敌,为了不让自己将来面临懵逼的困境,做个笔记总结一下。
本文讲解JavaScript各种继承方式和优缺点,欢迎各位提出不同意见探讨。
在类语言中,对象基于模板来创建,然后由类来实例化对象。在没有引入类概念的时候,我们通过构造函数来创建对象,然后通过函数来复用对象的属性和方法。这种方式虽然可以实现继承,但是它有许多缺点,例如:
- 容易产生重复代码
- 不利于代码维护
- 不利于代码的复用
为了解决这些问题,JavaScript引入了原型链的概念。原型链允许对象继承其他对象的方法和属性。这样,就可以实现代码的复用,而且可以很方便地对代码进行维护。
JavaScript中的继承主要有以下几种方式:
- 原型继承
- 构造函数继承
- 组合继承
- 类继承
下面我们分别来介绍一下这几种继承方式。
原型继承
原型继承是一种简单而常用的继承方式。它通过对象的原型链来实现继承。每个对象都有一个原型对象,原型对象是对象的父对象。对象的属性和方法都存储在原型对象中。当对象访问一个属性或方法时,如果对象本身没有这个属性或方法,那么它就会去原型对象中查找。
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.sayHello = function() {
console.log(`Hello, my name is ${this.name}.`);
};
const person1 = new Person('John', 20);
person1.sayHello(); // Hello, my name is John.
在上面的例子中,Person
函数的原型对象是Person.prototype
。person1
对象是Person
函数的实例,因此它的原型对象是Person.prototype
。当person1
对象调用sayHello
方法时,它会先在自身查找这个方法,如果找不到,就会去原型对象Person.prototype
中查找。
原型继承的优点是简单易用,而且可以实现代码的复用。它的缺点是,它不能继承对象的私有属性和方法。
构造函数继承
构造函数继承是另一种常用的继承方式。它通过构造函数来实现继承。子类的构造函数会调用父类的构造函数,这样子类就可以继承父类的属性和方法。
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.sayHello = function() {
console.log(`Hello, my name is ${this.name}.`);
};
function Student(name, age, major) {
Person.call(this, name, age);
this.major = major;
}
Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;
const student1 = new Student('John', 20, 'Computer Science');
student1.sayHello(); // Hello, my name is John.
在上面的例子中,Student
函数的构造函数会调用Person
函数的构造函数,这样Student
类就可以继承Person
类的属性和方法。Student
类的原型对象是Student.prototype
,它是Person
类的原型对象的子对象。
构造函数继承的优点是,它可以继承对象的私有属性和方法。它的缺点是,它需要显式地调用父类的构造函数,这可能会增加代码的复杂性。
组合继承
组合继承是原型继承和构造函数继承的结合。它通过组合使用原型继承和构造函数继承来实现继承。
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.sayHello = function() {
console.log(`Hello, my name is ${this.name}.`);
};
function Student(name, age, major) {
Person.call(this, name, age);
this.major = major;
}
Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;
Student.prototype.study = function() {
console.log(`I am studying ${this.major}.`);
};
const student1 = new Student('John', 20, 'Computer Science');
student1.sayHello(); // Hello, my name is John.
student1.study(); // I am studying Computer Science.
在上面的例子中,Student
函数的构造函数会调用Person
函数的构造函数,这样Student
类就可以继承Person
类的属性和方法。Student
类的原型对象是Student.prototype
,它是Person
类的原型对象的子对象。Student
类还定义了一个新的方法study()
。
组合继承的优点是,它可以继承对象的私有属性和方法,而且它可以实现代码的复用。它的缺点是,它需要显式地调用父类的构造函数,这可能会增加代码的复杂性。
类继承
类继承是ES6中引入的一种新的继承方式。它使用class
来定义类,然后使用extends
关键字来继承类。
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
sayHello() {
console.log(`Hello, my name is ${this.name}.`);
}
}
class Student extends Person {
constructor(name, age, major) {
super(name, age);
this.major = major;
}
study() {
console.log(`I am studying ${this.major}.`);
}
}
const student1 = new Student('John', 20, 'Computer Science');
student1.sayHello(); // Hello, my name is John.
student1.study(); // I am studying Computer Science.
在上面的例子中,Student
类继承了Person
类,因此它可以使用Person
类的方法和属性。Student
类还定义了一个新的方法study()
。
类继承的优点是,它简单易用,而且可以实现代码的复用。它的缺点是,它不支持多重继承。
继承方法的优缺点和使用场景
继承方法 | 优点 | 缺点 | 使用场景 |
---|---|---|---|
原型继承 | 简单易用 | 不能继承对象的私有属性和方法 | 需要继承对象的所有属性和方法时 |
构造函数继承 | 可以继承对象的私有属性和方法 | 需要显式地调用父类的构造函数 | 需要继承对象的私有属性和方法时 |
组合继承 | 可以继承对象的私有属性和方法 | 需要显式地调用父类的构造函数 | 需要继承对象的私有属性和方法时 |
类继承 | 简单易用 | 不支持多重继承 | 需要继承对象的所有属性和方法时 |