轻松深入理解JavaScript的继承原理

什么是继承?

继承这个词,在生活中有很多种语境,可以是继承财产,也可以是继承权利,或是继承血统,仔细想想他们有一个共同的特点就是,继承者所继承的事物均来自被继承者本身,其实JavaScript的继承也是这样的,很多编程中的思想都可以通过生活中的实例来理解

JavaScript的继承体现

那么在JavaScript中是怎么实现继承的呢?

类式继承

首先介绍第一种叫类式继承

// 类式继承

function Father() {
    
    
    this.FatherValue = true
}
//  为 Father添加一个共有方法:
Father.prototype.getFatherValue = function() {
    
    
    return this.FatherValue
}

function Son() {
    
    
    this.sonValue = false
}

//  sun 继承 father 
Son.prototype = new Father()

Son.prototype.getSonValue = function() {
    
    
    return this.sonValue
}

核心代码 :Son.prototype = new Father()

我们在实例化一个父类的时候。新创建的对象复制了父亲的构造函数里的属性和方法并且将原型_ _ proto_ _指向了父亲的原型对象,这样就拥有了父亲的原型对象上的属性和方法,并且这个新创建的对象可以直接访问到父亲原型对象上的属性与方法。

关于原型和原型链我是这样理解的
在这里插入图片描述
所以Son一层层找上去是可以访问到FatherValue的

但是这种类式的继承有个缺点

  1. 如果父类中的共有属性是引用类型,就会在子类中被所有实例共用,所以子类的实例更改子类原型从父类构造函数中继承来的共有属性就会直接影响其他子类
  2. 无法向父类传递参数,无法在实例化父类的时候对父类的构造函数内的属性进行初始化

构造函数继承

为了解决上述问题,所以有了构造函数继承

function Father(id){
    
    
    this.skill = ['read','run','speak']
    this.id = id
}
Father.prototype.showSkills = function(){
    
    
    console.log(this.skill)
}

function Son(id){
    
    
    // 继承 父类
    Father.call(this,id)
}

核心代码: Father.call(this,id)
call改变了函数的作用环境,将子类中的变量在父类中再执行一遍

缺点:
这种形式的继承创建出来的实例都会单独拥有一份不能共用

不能够实现代码复用,是不优雅的

组合继承

为了再一次对继承进行优化,我们把两种方法结合起来

function Father(id){
    
    
    this.skill = ['read','run','speak']
    this.id = id
}
Father.prototype.showSkills = function(){
    
    
    console.log(this.skill)
}

function Son(id , hobby){
    
    
    // 继承 父类id属性
    Father.call(this,id)
    this.hobby = hobby
}
// 子类原型继承父类
Son.prototype = new Father()

Son.prototype.getHobby = function(){
    
    
    console.log(this.hobby)
}

虽然上述的问题得到了基本解决,但是在使用构造函数继承时执行了一遍父类的构造函数,在实现子类的原型的类式继承的时候又调用了一遍父类的构造函数,调用了两遍,不够优雅

原型式继承

function inheritObj(o) {
    
    
    //声明一个过渡函数对象
    function F() {
    
    }
    // 过渡对象的原型继承父对象
    F.prototype = o
     // 返回过渡对象的一个实例,该实例的原型继承了父对象
    return new F()
}

在原型式继承之后,还有寄生式继承,以及寄生组合式继承,这里就不再赘述了,大家可以查阅资料作为拓展内容,接下来介绍一下继承函数的封装

圣杯模式

          /*
            **  圣杯模式:
            **  定义 F()作为中间层,用于实现Target 的个性化定制,并且不会影响到Father 的原型
            **  uber( super 是保留字,故此处不使用) 找到自己的超类  
            */ 
        function inherit(Target ,Origin){
    
    
            function F(){
    
    };
            F.prototype =  Origin.prototype;
            Target.prototype = new F();                 
            Target.prototype.constuctor = Target;
            Target.prototype.uber = Origin.prototype;    

        }

圣杯模式进化版

       /*
            **  此方法 来自于:YUI3 (  雅虎  )
            **  利用了函数闭包的性质
            **  var F = function () {} 成为了一个私有变量
            **  此方法更符合语义化  更加的高明
            */ 

            var inherit = (function(){
    
    

                var F = function(){
    
    };
                return function(Target,Origin){
    
    
                    F.prototype = Origin.prototype;
                    Target.prototype = new F();
                    Target.prototype.constuctor = Target;
                    Target.prototype.uber = Origin.prototype;
                }
            }());

猜你喜欢

转载自blog.csdn.net/qq_43377853/article/details/112643106