JavaScript的继承

构造函数的书写
在ES6之前,类的创建是利用构造函数来进行的。
下面的代码就是一个构造函数的书写,我们习惯将构造函数的首字母进行大写:

function Person(name, age) { this.name = name this.age = age }Person.prototype.running = function() { console.log(this.name + " running~") }const person = new Person('lisa', 18) person.running() // lisa running~

在这个代码当中,方法没有被直接书写在构造函数“当中”,而是写在原型当中。这是因为我们在创建实例对象的时候,会对其中的属性和方法分配存储空间,然而方法是一样的,如果每次创建实例对象都给方法分配空间就会有点浪费,但是写在原型当中就不会存在这个情况,当实例对象调用方法的时候,他会去到原型当中寻找到该方法,那个方法从始至终都只有那“一个”。
多个构造函数的属性和方法重叠
有时候我们书写构造函数时,会发现它们存在相同的属性或者方法,就像下面的两个构造函数一样:
Teacher类:
function Teacher(name, age, title) { this.name = name this.age = age this.title = title }Teacher.prototype.eatting = function() { console.log(this.name + " eatting~") }Teacher.prototype.teaching = function() { console.log(this.name + " tearching~") }

Student类:
function Student(name, age, sno) { this.name = name this.age = age this.sno = sno }Student.prototype.eatting = function() { console.log(this.name + " eatting~") }Student.prototype.studying = function() { console.log(this.name + " studying~") }

我们可以发现Teacher类和Student类当中的name、age属性以及eatting出现了重叠,这个时候就可以利用面向对象当中的封装与继承思想了,将相同的属性和方法封装成一个父类,再让Teacher类和Student继承于它。
在ES6之前是不存在extends这个继承关键字,下来就说一下基于原型链的继承方法。
(1)利用构造函数
function Person(name, age) { this.name = name this.age = age }Person.prototype.eatting = function() { console.log(this.name +" eatting~") }//子类 function Student(name, age, sno) { Person.call(this, name, age) this.sno = sno }Student.prototype = new Person()Student.prototype.studying = function() { console.log(this.name + " studying~") }const stu = new Student('lisa', 18, 111) stu.eatting() stu.studying()

很显然,在这种方法当中,我们调用了两次父类的构造函数。
(2)原型式继承
这种想法可以用下面图来进行展示:
JavaScript的继承
文章图片

Teacher类和Student类两个构造函数的原型都指向各自的中间原型,而中间原型最终指向了Person类。
根据这个想法,我们可以写一个用于继承的函数:
function inherit(obj) { function middleFn() {} middleFn.prototype = new obj() return new middleFn() }function Person(name, age) { this.name = name this.age = age }Person.prototype.running = function() { console.log(this.name + " running~") }function Student(sno) { this.sno = sno }Student.prototype = inherit(Person)Student.prototype.studying = function() { console.log(this.name + " studying~") }const stu = new Student('lisa', 18, 1111)stu.running() // lisa running~ stu.studying() // lis studying~

在这个函数当中,我们设计了一个中间构造函数,它的原型指向了传进来的构造函数,这个继承函数最后返回了中间构造函数的实例对象。
这个时候我们就只需要将Student构造函数的原型指向返回的实例对象就可以完成图中的模式。
这个inherit函数还可以利用Object当中的setPrototypeOf进行书写,也就是下面的方法:
function inherit2(obj) { const newObj = {} Object.setPrototypeOf(newObj, obj) return newObj }const obj = { name: 'peter' }const obj2 = inherit2(obj) console.log(obj2.name) // peter

亦或者是利用Object.create()方法:
function inherit3(obj) { const newObj = Object.create(obj)//返回一个对象,并且这个对象的原型会指向obj return newObj }const obj = { name: 'peter' }const obj1 = inherit3(obj)console.log(obj1.name)

(3)寄生组合模式
function inherit(obj) { function middleFn() {} middleFn.prototype = new obj() return new middleFn() }function inheritPrototype(sub, sup) { sub.prototype = inherit(sup) sub.prototype.constructor.name = sub }inheritPrototype(Student, Person)

【JavaScript的继承】最后将sub的原型当中的constructor指向了sub

    推荐阅读