likes
comments
collection
share

面试:了解继承嘛?继承有那些方法,优缺点呢?

作者站长头像
站长
· 阅读数 18

前言

      在面试中有一个非常热门的知识点——继承,那么什么是继承呢?通俗来讲就是让子类的实例对象能访问到父类上的属性,但是我们再JacaScript中如何实现呢?下面Virtual09将会为大家介绍我们如何在JavaScript中实现继承

正文

原型链继承

      众所周知,构造函数new出来的对象会隐式继承到构造函数原型上的属性。 那么我们来看看这串代码

function Parent(){
    this.name = 'Tom',
    this.like = [1,2,3]
}
function Child(){
    this.type = 'children'
}
Child.prototype = new Parent()

let s1 = new Child()
console.log(s1.name);

我们把new Parent()挂在Child的原型上,因为JavaScript引擎在查找属性时,会顺着对象的隐式原型向上查找,找不到,则查找隐式原型的隐式原型,一直向上,直到找到null为止。所以在Child中是不存在name这个属性的,JavaScript引擎就会顺着对象的隐式原型上去找属性,也就是会去Parent里面找。所以结果是打印的是

面试:了解继承嘛?继承有那些方法,优缺点呢?

而且对于原型我们知道的是实例对象可以修改显示继承到的属性,但是无法修改隐式继承到的属性(原型上的),所以我们来看看,当执行s1.like.push(4)之后,打印s1.like的结果是什么?

面试:了解继承嘛?继承有那些方法,优缺点呢?

面试:了解继承嘛?继承有那些方法,优缺点呢?

结果是[1,2,3,4],不是说实例对象无法修改隐式原型上的数据嘛?这里我们需要注意的是s1拿到数组like去帮它调用数组自己身上自带的push()方法,导致了隐式原型上的数据被修改,并不是s1实例对象修改的。所以我们可以使用原型链的方式实现继承,但是当我们再实例一个s2,同样打印like

面试:了解继承嘛?继承有那些方法,优缺点呢?

拿到的数据依旧是[1,2,3,4]并不是我们要的[1,2,3]所以原型链继承的缺点就是子类实例会继承同一个原型对象,内存共享,所以实例之间会相互影响。

构造函数继承

      现在我们来看看,构造函数继承,是如何实现的?

function Parent(){
    this.name = 'Tom',
    this.like = [1,2,3]
}
function Child(){
    Parent.call(this)
    this.type = 'children'
}


let s1 = new Child()

我们通过调用Parent.call()Parentthis指向Child,等效于Child也拥有Parent的属性,这样我们也可以访问到Parent上面的属性。

面试:了解继承嘛?继承有那些方法,优缺点呢?

但是,这样我们就无法通过原型链访问到Parent隐式原型上属性和方法了。因为我们调用构造函数,就不会存在一个原型链供我们使用,去找到Parent隐式原型上的属性和方法,既然不存在原型链,那我们去修改数组中的值,也不会说,创建了一个s2,这个s2调用的数组会等于s1调用数组,它们不会共享数据。所以对于构造函数继承子类实例之间不会相互影响,无法继承到父类原型上的属性和方法,只能继承父类的属性和方法。

组合继承

其实也就是原型链加上构造函数一起使用就可以了。

Parent.prototype.getName = function(){
    return this.name
}
function Parent(){
    this.name = 'Tom',
    this.like = [1,2,3]
}
function Child(){
    Parent.call(this)
    this.type = 'children'
}
Child.prototype = new Parent()

let s1 = new Child()

console.log(s1.getName());

这样写,会存在一个问题,也是就按理来说s1的构造函数应该是Child,但是这里我们可以看到

面试:了解继承嘛?继承有那些方法,优缺点呢?

它这里显示的构造函数是Parent,是因为我们把new Parent()这个实例对象,给到了Child.prototype,而new Parent()的构造函数是Parent,所以导致了我们看到的s1的构造函数是Parent,所以我们要手动设置一下Child.prototype.constructor = Child 我们这样写,解决了原型链继承,以及构造函数继承带来的问题,但是自己也存在问题,也就是父类被二次调用了。

原型式继承

我们来看看对象的继承

let parent = {
    name: 'Tom',
    age:40,
    like:[1,2]
}

let child = Object.create(parent);

我们使用Object.create()创建了一个新的对象,我们把对象parent作为新创建的对象的原型,也就是说,我们child可以通过原型链访问到parent里面的数据,但是提到原型链,我们就会知道内容是可以共享Object.create(x) 缺点: 多个实例之间,继承到的引用类型是相同的,会相互影响浅拷贝,会把引用类型的属性拷贝一份,值类型的属性会共享(深拷贝的引用地址就会变)

寄生式继承

let parent = {
    name: 'Tom',
    age:40,
    like:[1,2]
}


function clone(obj){
    let clone = Object.create(obj);
    clone.getLike = function(){
        return this.like
    }
    return clone;
}

let child = clone(parent)

寄生式继承的特点就是同原型式继承,但是可以让子对象继承父对象,并且可以添加一些新的属性和方法

寄生组合继承

Parent.prototype.getName = function(){
    return this.name
}
function Parent(){
    this.name = 'Tom',
    this.like = [1,2,3]
}
function Child(){
    Parent.call(this)
    this.type = 'children'
}
Child.prototype = Object.create(Parent.prototype)
Child.prototype.constructor = Child

let s1 = new Child()
let s2 = new Child()

寄生组合继承就是使用Object.create(Parent.prototype)创建一个新的对象,这个对象的原型就是Parent.prototype。保证了我们修改Child原型上的值时不会修改到parent.prototype上的值。

本文到此也就结束了,感谢大家阅读,若有不足,恳请支持,谢谢大家!!

转载自:https://juejin.cn/post/7393311009686306835
评论
请登录