JavaScript中继承的实现方式

Posted 橘猫吃不胖~

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaScript中继承的实现方式相关的知识,希望对你有一定的参考价值。

javascript中继承的实现方式

1 构造函数式继承

构造函数式继承就是在子类中执行父类的构造函数,并为其传递参数,这个过程可以使用call()函数来实现。

示例代码:定义Person类(父类),它有自己的属性name和age,还有自己的方法getName()和getAge()

function Person(name, age) 
    this.name = name; // 初始化姓名和年龄
    this.age = age;

Person.prototype.getName = function ()  // 获得姓名
    console.log("我的名字" + this.name);

Person.prototype.getAge = function ()  // 获得年龄
    console.log("我的年龄:" + this.age);

接下来定义子类Student,在子类中调用父类的构造函数,获得父类的属性和方法:

function Student(name, age, grade) 
    Person.call(this, name, age); // 将参数传递给父类
    this.grade = grade; // 还可以定义自己的属性

但是这样的方式,当初始化子类的对象后,该对象无法获取父类的方法getName()和getAge(),因为父类的方法在原型链上,使用call()方法只获取到了父类本身的环境,并没有获取到父类的原型

let s1 = new Student("张三", 20, 100);
s1.getName(); // TypeError: s1.getName is not a function
s1.getAge(); // TypeError: s1.getAge is not a function

2 类式继承(原型式继承)

构造函数继承无法继承父类在原型链上的方法,那么类式继承便可以使用父类的方法了。

示例代码:定义Person类(父类),它有自己的属性name和age,还有自己的方法getName()和getAge()

function Person(name, age) 
    this.name = name; // 初始化姓名和年龄
    this.age = age;

Person.prototype.getName = function ()  // 获得姓名
    console.log("我的名字" + this.name);

Person.prototype.getAge = function ()  // 获得年龄
    console.log("我的年龄:" + this.age);

接下来定义子类Student,子类的原型指向了父类的实例对象,子类的构造函数再指回自己:

function Student(grade) 
    this.grade = grade;

Student.prototype = new Person(); // 子类的原型指向父类的实例对象
Student.prototype.constructor = Student; // 子类原型的构造函数再指回子类

类式继承也有一些问题:
①类的原型对象上出现了父类构造函数中的属性和方法,不仅多余,而且是错的。

let s1 = new Student("张三", 20, 100); // 实例化子类
console.log(s1);


②多执行了一次构造函数。如果有很多次继承,那么就会不停地执行构造函数,对资源是一种浪费。

③无法复用构造函数中存储属性的逻辑。子类的属性的初始化与父类没有关系。如果不在子类中初始化name和age,那么就获取不到具体的name和age值,而是显示undefined。

function Student(grade) 
    this.grade = grade;

Student.prototype = new Person(); // 子类的原型指向父类的实例对象
Student.prototype.constructor = Student; // 子类原型的构造函数再指回子类

let s1 = new Student("张三", 20, 100);
s1.getName(); // 我的名字undefined
s1.getAge(); // 我的名字undefined

3 组合式继承

组合式继承就是构造函数继承和类式继承的结合,它的问题就是类式继承拥有的问题。

示例代码:定义Person类(父类),它有自己的属性name和age,还有自己的方法getName()和getAge()

function Person(name, age) 
    this.name = name; // 初始化姓名和年龄
    this.age = age;

Person.prototype.getName = function ()  // 获得姓名
    console.log("我的名字" + this.name);

Person.prototype.getAge = function ()  // 获得年龄
    console.log("我的年龄:" + this.age);

将构造函数式继承和类式继承结合起来实现子类:

function Student(name, age, grade) 
    Person.call(this, name, age);
    this.grade = grade;

Student.prototype = new Person(); // 子类的原型指向父类的实例对象
Student.prototype.constructor = Student; // 子类原型的构造函数再指回子类

它的问题如下:
①类的原型对象上出现了父类的构造函数和方法
②多执行了一次构造函数

4 寄生式继承

在一个继承方法中,创建一个寄生类,让寄生类的原型,等于父类的原型,再实例化寄生类,赋值给子类的原型,这叫做寄生式继承。

寄生类代码如下:

function inherit(child, parent)  // 定义寄生类
    function F() 
        this.constructor = child; // 修改构造函数
    ;
    F.prototype = parent.prototype; // 寄生类的原型等于父类的原型
    child.prototype = new F(); // 寄生类示例为子类复制
    return child;

那么接下来使用一下寄生类,首先定义父类和子类,并且将子类和父类传入寄生类:

// 父类
function Person(name, age) 
    this.name = name; // 初始化姓名和年龄
    this.age = age;

Person.prototype.getName = function ()  // 获得姓名
    console.log("我的名字" + this.name);

Person.prototype.getAge = function ()  // 获得年龄
    console.log("我的年龄:" + this.age);


// 子类
function Student(name, age, grade) 
    this.name = name;
    this.age = age;
    this.grade = grade;


inherit(Student, Person); // 继承

这时创建一个子类的实例对象,查看子类的实例对象,我们发现它解决了组合式继承的问题:①类的原型对象上出现了父类的构造函数和方法;②多执行了一次构造函数

let s1 = new Student("张三", 20, 100);
console.log(s1);


但是它的问题是无法复用构造函数中存储属性的逻辑。与类式继承第③条问题相同。

5 寄生组合式继承

寄生组合式继承就是综合使用构造函数式继承和寄生式继承。在子类中调用一下父类的构造函数。

示例代码:

function inherit(child, parent)  // 定义寄生类
    function F() 
        this.constructor = child; // 修改构造函数
    ;
    F.prototype = parent.prototype; // 寄生类的原型等于父类的原型
    child.prototype = new F(); // 寄生类示例为子类复制
    return child;


function Person(name, age) 
    this.name = name; // 初始化姓名和年龄
    this.age = age;

Person.prototype.getName = function ()  // 获得姓名
    console.log("我的名字" + this.name);

Person.prototype.getAge = function ()  // 获得年龄
    console.log("我的年龄:" + this.age);


function Student(name, age, grade) 
    Person.call(this, name, age);
    this.grade = grade;


inherit(Student, Person);

寄生组合式继承是ES6之前最好的继承方式了,ES6出现之后使用class关键字来声明一个类,用extends关键字来继承父类。

以上是关于JavaScript中继承的实现方式的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript中继承的实现

JavaScript中继承的多种方式和优缺点

C++和Java中继承的个人感觉

js中继承的实现,原型链的知识点归纳,借用构造函数,组合继承(伪经典继承)

ES5和ES6中继承的不同之处

JavaScript继承的几种实现