js基础知识之继承与闭包

103 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第17天,点击查看活动详情

继承

在js中,继承有很多种方式,这些不同的方式也有不同的利弊,我们来了解一下继承的种类和优缺点。

原型链继承

当对象的原型对象指向了另一个对象时,那么当前对象就会继承原型对象的内容

function a (){
    this.name = ['aaa']
}
a.prototype.getName = function(){
    return this.name
}
function b (){
    
}
b.prototype = new a()

let child1 = new b()
let child2 = new b()
child1.name.push('ccc')
console.log(child2.getName())
//(2) ['aaa', 'ccc']

原型链的缺点就是引用类型的对象会被继承对象共同使用,导致修改一个对象的属性会影响到其他对象的对应属性。

经典继承

在函数内通过call函数来指向被继承对象以达到继承目的

function a (){
    this.name = ['aaa']
}
function b (){
    return a.call(this)
}

let child1 = new b()
let child2 = new b()
child1.name.push('bbb')
//['aaa']
console.log(child2.name)
//(2) ['aaa', 'bbb']
console.log(child1.name)

相比原型链继承来说,优点是避免了对象之间属性的互相影响,缺点则是方法都得放在构造函数里面,每次创建实例都得创建一次方法

组合继承

是经典继承和原型链继承的融合方法,继承了两者的优点避免了缺点。

function a (){
    this.name = ['aaa']
}
a.prototype.getName = function(){
    return this.name
}
function b (aaa){
    a.call(this,aaa)
}

b.prototype = new a()
let child1 = new b()
let child2 = new b()
child1.name.push('bbb')
//['aaa']
console.log(child2.name)
//(2) ['aaa', 'bbb']
console.log(child1.name)

相比原型链继承和经典继承来说,组合继承既不会使实例之间属性互相影响,又不会导致每次创建实例都需要创建一遍方法。缺点就是会调用两次父函数的构造方法

寄生组合继承

寄生组合继承呢则是要避免组合继承中调用两次父函数的缺点。

function a (){
    this.name = ['aaa']
}
a.prototype.getName = function(){
    return this.name
}
function b (aaa){
    a.call(this,aaa)
}

function c (){}
c.prototype = a.prototype
b.prototype = new c()

let child1 = new b()
let child2 = new b()
child1.name.push('bbb')
//['aaa']
console.log(child2.name)
//(2) ['aaa', 'bbb']
console.log(child1.name)

这种方式虽然需要创建一个空函数,但并不会多次调用父函数的构造函数。

闭包

在js的面试中,什么是闭包这个问题可以说是一个非常经典的问题了,闭包的定义是什么,目前的说法是两个条件

  • 函数中有使用到自由变量即不属于函数内部的变量
  • 函数在创建上下文销毁后,函数依然存在
function a () {
    var aa = 'aaa'
    function b (){
        console.log(aa)
    }
    return b
}
let c = a()
// aaa
c()

那么在什么时候会出现闭包呢,当我们有一个函数返回了一个内部函数,这个内部函数又调用了函数内的变量,则就构成了闭包,那么它有什么实际应用场景呢,经典的就是防抖节流函数,我们可以通过闭包来更好的实现。