Javascript继承关系的实现方法
JavaScript 继承关系的实现方法有多种,下面介绍几种常用的方法:
1. 原型链继承
在 JavaScript 中每个对象都有一个原型对象,我们可以通过原型对象来实现继承。原型链继承就是通过将子类的原型指向父类实例来实现继承的。
代码示例:
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function() {
console.log('Hello, ' + this.name + '!');
}
function Student(name) {
}
Student.prototype = new Person();
var s = new Student('Tom');
s.sayHello(); // output: Hello, Tom!
注意事项:
- 子类的原型对象会继承父类的所有属性和方法,包括父类的构造函数,这可能会导致一些意外的结果。
- 子类不能向父类的构造函数传递参数。
2. 构造函数继承
构造函数继承是通过在子类构造函数内部调用父类构造函数来实现的。这种方式能够避免原型链继承的缺陷,但是也存在一些问题。
代码示例:
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function() {
console.log('Hello, ' + this.name + '!');
}
function Student(name) {
Person.call(this, name);
}
var s = new Student('Tom');
s.sayHello(); // output: Uncaught TypeError: s.sayHello is not a function
注意事项:
- 父类的实例方法和属性不能被子类原型继承,只能在子类构造函数中定义。
- 父类的静态方法和属性也不能被子类继承。
3. 组合继承
组合继承是将原型链继承和构造函数继承结合起来的一种方式。它既可以继承父类实例上的属性和方法,也可以继承父类原型上的属性和方法。
代码示例:
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function() {
console.log('Hello, ' + this.name + '!');
}
function Student(name) {
Person.call(this, name);
}
Student.prototype = new Person();
Student.prototype.constructor = Student;
var s = new Student('Tom');
s.sayHello(); // output: Hello, Tom!
注意事项:
- 此方法的缺点是会调用两次父类构造函数,一次是在生成子类原型时调用,一次是在子类构造函数中调用。
- 可以优化为使用 Object.create() 方法来创建子类原型对象:Student.prototype = Object.create(Person.prototype);
4. 原型式继承
原型式继承是通过克隆一个已有的对象来生成一个新对象,并对新对象进行修改后得到的。这种继承方式不需要定义类,只需要创建一个对象即可。
代码示例:
var person = {
name: 'Tom',
sayHello: function() {
console.log('Hello, ' + this.name + '!');
}
};
var student = Object.create(person);
student.name = 'Jerry';
student.sayHello(); // output: Hello, Jerry!
注意事项:
- 原型式继承存在浅拷贝的问题,即如果原型对象中嵌套了一个对象,它们都会被共享。
- 原型式继承并没有解决构造函数继承中的问题。
5. 寄生式继承
寄生式继承是在原型式继承的基础上增强了对象,使其具有更强的功能。
代码示例:
function createStudent(person) {
var student = Object.create(person);
student.sayHello = function() {
console.log('Hello, ' + this.name + '!');
};
return student;
}
var person = {
name: 'Tom'
};
var student = createStudent(person);
student.name = 'Jerry';
student.sayHello(); // output: Hello, Jerry!
注意事项:
- 寄生式继承本质上是一种封装继承,所以具有封装继承的所有缺点。
- 由于新对象和原型对象之间并不存在真正的关联,所以不能用 instanceof 来判断对象类型。
6. 继承 ES6 的 class 语法
class 是 ES6 中新增的语法,可以更方便地实现面向对象编程。使用 class 定义继承关系,可以避免一些传统继承方式的问题。
代码示例:
class Person {
constructor(name) {
this.name = name;
}
sayHello() {
console.log('Hello, ' + this.name + '!');
}
}
class Student extends Person {
constructor(name, score) {
super(name);
this.score = score;
}
study() {
console.log(this.name + ' is studying!');
}
}
let s = new Student('Tom', 80);
s.sayHello(); // output: Hello, Tom!
s.study(); // output: Tom is studying!
注意事项:
- class 语法是 ES6 新增的,需要使用 babel 等工具进行转换才能在低版本浏览器中使用。
- class 语法实现继承的本质仍然是原型链继承。
