继承
原型链继承
原型链继承是指将父类的实例作为子类的原型,使得子类可以访问父类原型中的属性和方法。实现代码如下:
function Parent() {
this.name = "parent";
}
Parent.prototype.sayHello = function () {
console.log(`Hello, I'm ${this.name}`);
};
function Child() {
this.age = 18;
}
Child.prototype = new Parent();
Child.prototype.constructor = Child;
var child = new Child();
child.sayHello(); // Hello, I'm child
上面代码中,我们先定义了一个 Parent 构造函数,并在其原型对象上添加了一个 sayHello 方法。然后,定义了一个 Child 构造函数,并将 Parent 的实例赋值给 Child 的原型对象,从而实现了原型链继承。最后,我们创建了一个 Child 的实例,并调用其 sayHello 方法,输出了 Hello, I'm child。
原型链继承的缺点是,在创建子类实例时,无法向父类构造函数传递参数,因为父类的实例已经被赋值给了子类的原型对象。
构造函数继承
构造函数继承是指在子类构造函数内调用父类构造函数,并使用 call 或 apply 方法改变其执行上下文,从而实现继承父类属性的目的。实现代码如下:
function Parent() {
this.name = "parent";
}
function Child() {
Parent.call(this);
this.age = 18;
}
var child = new Child();
console.log(child.name); // parent
console.log(child.age); // 18
上面代码中,我们先定义了一个 Parent 构造函数,其中定义了一个 name 属性。然后,定义了一个 Child 构造函数,并在其内部调用了 Parent 构造函数,并使用 call 方法将其执行上下文改变为 Child 实例,从而实现了继承父类属性的目的。最后,我们创建了一个 Child 的实例,并输出了其 name 和 age 属性值。
构造函数继承的缺点是,在子类的原型对象上无法访问到父类的方法。
组合继承
组合继承是将原型链继承和构造函数继承结合起来使用,既可以继承父类的属性,也可以继承父类原型中的方法。实现代码如下:
function Parent() {
this.name = "parent";
}
Parent.prototype.sayHello = function () {
console.log(`Hello, I'm ${this.name}`);
};
function Child() {
Parent.call(this);
this.age = 18;
}
Child.prototype = new Parent();
Child.prototype.constructor = Child;
var child = new Child();
child.sayHello(); // Hello, I'm child
上面的代码实现了组合继承。我们先定义了一个 Parent 构造函数,并在其原型对象上添加了一个 sayHello 方法。然后,定义了一个 Child 构造函数,并在其内部调用了 Parent 构造函数,并使用 call 方法将其执行上下文改变为 Child 实例,从而实现了继承父类属性的目的。接着,将 Parent 的实例赋值给 Child 的原型对象,从而实现了继承父类原型中的方法。最后,我们创建了一个 Child 的实例,并调用其 sayHello 方法,输出了 Hello, I'm child。
组合继承是目前使用最广泛的继承方式。它既可以继承父类的属性,也可以继承父类原型中的方法。但是它也存在一定的缺点,即在创建子类实例时,会调用两次父类构造函数,导致效率低下。因此,在 ES6 中出现了一种新的继承方式,即类继承。
寄生组合继承
寄生组合继承是对组合继承的优化。它通过将父类的原型对象进行浅复制,然后将复制后的原型对象赋值给子类的原型对象,从而避免了重复调用父类构造函数的问题。实现代码如下:
function Parent() {
this.name = "parent";
}
Parent.prototype.sayHello = function () {
console.log(`Hello, I'm ${this.name}`);
};
function Child() {
Parent.call(this);
this.age = 18;
}
// 创建一个空函数 F
function F() {}
// 将父类的原型对象浅复制到 F 的原型对象上
F.prototype = Parent.prototype;
// 将 F 的实例赋值给 Child 的原型对象
Child.prototype = new F();
// 修复 Child 的原型对象的 constructor 属性
Child.prototype.constructor = Child;
var child = new Child();
child.sayHello(); // Hello, I'm child
上面代码中,我们定义了一个空函数 F,并将父类的原型对象浅复制到 F 的原型对象上。然后,将 F 的实例赋值给 Child 的原型对象,从而实现了避免重复调用父类构造函数的目的。最后,我们创建了一个 Child 的实例,并调用其 sayHello 方法,输出了 Hello, I'm child。
寄生组合继承是目前最优秀的继承方式,它既避免了原型链继承和构造函数继承的缺点,又保留了它们的优点。
ES6 的 extends
ES6 中引入了类(class)的概念,提供了一种更加简洁、易读的方式实现面向对象编程。在类的继承方面,ES6 提供了 extends 关键字,用于实现子类对父类的继承。
ES6 的类继承使用 extends 关键字实现,示例代码如下:
class Parent {
constructor(name) {
this.name = name;
}
sayHello() {
console.log(`Hello, I'm ${this.name}`);
}
}
class Child extends Parent {
constructor(name, age) {
super(name);
this.age = age;
}
}
s;
var child = new Child("child", 18);
child.sayHello(); // Hello, I'm child
上面代码中,我们定义了一个 Parent 类和一个 Child 类,其中 Child 类通过 extends 关键字继承了 Parent 类。在 Child 类的构造函数中,通过 super 方法调用了父类的构造函数,并传递了 name 参数。这样,就实现了子类对父类属性的继承。在 Child 类中,我们没有定义任何方法,因此它会自动继承父类中的方法。
ES6 的类继承使用 extends 关键字实现,它相比于传统的继承方式,代码更加简洁易读,同时也避免了传统继承方式的一些问题。