继承
传统的面向对象语言的继承主要有两种方式:接口继承和实现继承。而JavaScript
与传统的面向对象比较其实现继承主要是基于原型链的实现继承方式。因为本质上JavaScript
的函数也是对象,没有函数签名,无法实现接口继承。
原型链继承
原型链实现继承,采用的方式就是重写子类型的原型。因为我们知道对象属性和方法查询,是先查找实例对象上的属性和方法,如果没有找到会继续通过原型链逐级向上层查找直到查找到默认原型Object.prototype
为止。
function Parent() {
this.parentType = 'parent';
}
Parent.prototype.walk = function () {
console.log('this is parent!')
}
function Son() {
this.sonType = 'son';
}
Son.prototype = new Parent();
const son = new Son();
console.log(son);
存在的问题:
原型链继承虽然能实现继承操作,但是存在一些缺陷。原型本身就是为了共享方法和属性,这这些共享的方法和属性是提供给所有的实例使用,也正是这种共享性使得在原型上修改任何的方法和属性会应用到所有的实例上。第二个比较大的问题就是,无法像超类型的构造函数传递参数。
借用构造函数继承
借用构造函数就是在子类型中执行超类型的构造函数。
function Parent(name) {
this.name = name;
}
function Son(name, age) {
this.age = age;
Parent.call(this, name);
}
const son = new Son('Jam', 27);
console.log(son);
存在的问题:
主要是无法实现属性和方法的共享,所有的方法和属性都定义在this实例上,造成不必要的开销。
组合继承
组合继承即是将原型链继承和借用构造函数继承组合在一起使用,既通过原型链实现属性和方法的共享,有通过构造函数实现实例属性和方法的传递。
function Parent(name) {
this.name = name;
}
Parent.prototype.walk = function () {
console.log('this is parent!');
}
function Son(name, age) {
this.age = age;
Parent.call(this, name);
}
Son.prototype = new Parent();
Son.prototype.constructor = Son;
Son.prototype.jump = function () {
console.log('this is son!');
}
const son = new Son('Jam', 27);
console.log(son);
组合继承比较好的发挥了原型链继承和借用构造函数继承的优势,因此也是推荐的继承方式。
Class语法继承
ES6
引入了Class
语法,使得JavaScript
与传统的面向对象语言更相向。ES6
的class
可以看作只是一个语法糖,它的绝大部分功能,ES5
都可以做到,新的class
写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。
class Parent {
constructor(name) {
this.name = name;
}
walk() {
console.log('this is parent!');
}
}
class Son extends Parent {
constructor(name, age) {
super(name);
this.age = age;
}
jump() {
console.log('this is son!');
}
}
const son = new Son('Jam', 27);
console.log(son);
随着ES
版本的不断演进,新的语法不断的引用,配套的编译工具的发展,JavaScript
中创建自定义对象,实现继承都在使用Class
语法,也使得其成为事实上的标准方式。