原型链
原型链是 JavaScript 实现继承的基础。每个对象都有一个原型对象,通过原型对象可以访问到父类的属性和方法。原型链的终点是 Object.prototype,它的原型是 null。
构造函数
构造函数是用于创建对象的特殊函数。通过 new 关键字调用构造函数,可以创建一个新的对象实例,并将构造函数中的 this 绑定到新创建的对象上。
ES6 class
ES6 引入的 class 语法是对原型链和构造函数的语法糖,使得继承变得更加简洁和直观。通过 extends 关键字可以实现继承,通过 super 关键字可以调用父类的构造函数和方法。
继承方式总结
1)原型链继承:
主要通过将子类的原型(prototype)指向父类的实例来实现。
优点是实现简单,缺点是所有实例共享父类属性,修改子类实例会影响到所有实例。
2)借用构造函数继承:
在子类构造函数中调用父类构造函数,通过Call或Apply传递参数。
优点是每个实例都有自己的属性,不会共享父类的属性,但不能继承父类的原型方法。
3)组合继承:
结合原型链和借用构造函数的优点,使用原型链继承父类的原型方法,用借用构造函数继承父类的属性。
缺点是调用两次父类构造函数,开销较大。
4)寄生组合继承:
结合了组合继承的优点,同时解决了调用两次父类构造函数的问题。
是目前使用最多、最推荐的方法。
5)ES6的class继承:
更加简洁和语法糖,使得继承更易读、更易写,而且天然支持实现组合继承。
比传统的原型继承和组合方法更直观,有利于代码维护和理解
代码
//原型链继承------------------------------------------
/**
* 优点是实现简单,
* 缺点是父对象的原型上包含引用类型的属性,那么所有子对象都会共享这个引用类型属性。
* 这意味着,如果一个子对象修改了原型上的引用类型属性,那么其他子对象也会受到影响。
*/
function Coder () {
this.type = 'Coder'
this.test={
name:"coder"
}
}
Coder.prototype.rap = function () {
console.log('yo yo yo');
}
function Yupi(name) {
this.name = name;
this.age = 18;
}
Yupi.prototype = new Coder();
Yupi.prototype.constructor = Yupi;
//测试
const yupi = new Yupi("yupi");
// yupi.rap();
// console.log(yupi.type);
//修改子类实例属性,测试是否影响其他子类实例
const yupi2 =new Yupi("yupi2");
console.log(yupi.test.name);
yupi2.test.name="coder2";
console.log(yupi.test.name);
//class继承----------------------------------------------
class Coder {
constructor()
{
this.type = "coder"
}
rap()
{
console.log("yo yo yo");
}
}
class Yupi extends Coder
{
constructor(name)
{
super();//调用父类的构造函数
this.name = name;
this.age = 18;
}
}
// 测试
const yupi = new Yupi('Yupi');
console.log(yupi.type); // 输出: Coder
yupi.rap(); // 输出: yo yo yo
// //构造函数继承--------------------------------------
// /**
// * 构造函数继承是通过在子类的构造函数中调用父类的构造函数来实现的。
// * 这样,子类的实例就可以继承父类的属性。
// 优点是每个实例都有自己的属性,不会共享父类的属性,但不能继承父类的原型方法。
// */
function Coder()
{
this.type="coder ?"
}
Coder.prototype.rap=function()
{
console.log("ha ha ha");
}
function Yupi(name)
{
Coder.call(this);//调用父类构造函数
this.name = name;
this.age = 18;
}
// 测试
const yupi = new Yupi('Yupi');
console.log(yupi.type); // 输出: Coder
// yupi.rap(); // 这行代码会报错,因为构造函数继承无法
//组合继承---------------------------------------------------------
/**
* 组合继承结合了原型链继承和构造函数继承的优点。
* 它通过调用父类构造函数继承属性,
* 通过原型链继承继承方法。
缺点是调用两次父类构造函数,开销较大。
*/
function Coder() {
this.type = 'Coder';
}
Coder.prototype.rap = function () {
console.log('yo yo yo');
};
function Yupi(name) {
Coder.call(this); // 调用父类构造函数,继承父类的属性
this.name = name;
this.age = 18;
}
// 组合继承
Yupi.prototype = new Coder(); //继承父类方法
Yupi.prototype.constructor = Yupi;
// 测试
const yupi = new Yupi('Yupi');
console.log(yupi.type); // 输出: Coder
yupi.rap(); // 输出: yo yo yo
//寄生组合继承-------------------------------------------
/**
* 寄生组合继承是对组合继承的一种优化。
* 它避免了组合继承中父类构造函数被调用两次的问题。
是目前使用最多、最推荐的方法。
*/
function Coder() {
this.type = 'Coder';
}
Coder.prototype.rap = function () {
console.log('yo yo yo');
};
function Yupi(name) {
Coder.call(this); // 调用父类构造函数
this.name = name;
this.age = 18;
}
// 寄生组合继承
Yupi.prototype = Object.create(Coder.prototype);//创建一个新对象,这个新对象的原型指向父类的原型
Yupi.prototype.constructor = Yupi;
// 测试
const yupi = new Yupi('Yupi');
console.log(yupi.type); // 输出: Coder
yupi.rap(); // 输出: yo yo yo