JS完美收官之——继承发展史

代码复用一直是我们程序员所追求的远大目标,毕竟可以少写点代码,何乐而不为呢?当说到代码复用的时候,最先想到的是继承,JavaScript对象上有自己的属性,也有一些属性是从原型对象继承来的,下面我们来看看实现继承的几种方式:

1.传统模式——原型链

用原型链的继承时,不仅继承了自身的属性,而且继承了原型上的属性和方法。

看代码:

 //原型链继承
        Grand.prototype.lastName = '鲁班大师';
        function Grand(){
            //隐式链接 __proto__
        }
        var grand = new Grand();
        Father.prototype = grand;
        function Father(){
            this.name = '小鲁班';
             //隐式链接 __proto__
        }
        var father = new Father();

        Son.prototype = father;
        function Son(){
          this.age = 18
            //隐式链接 __proto__
        }
        var son = new Son();

我们先看下这种继承模式的工作原理:

当我们用new Son()来创建一个对象时,会创建一个图(3),里面保存了age的属性,如果我们访问lastName属性时,尽管图(3)并没有lastName这个属性,但是通过Son()的prototype原型上的隐式属性__proto__可以访问到图(1)中的lastName属性,这个__proto__就是原型链,这个属性只能系统内部去使用,开发者是用不了的。

我们还要注意一点:如果我们在控制台执行,son.name = "长江七号",这个操作并不会改变图(2)里面的name,它会直接在图(3)自身上创建一个属性,如下:

原型链继承弊端:缺点在于同时继承了两个对象的属性,但是在大多的时候我们并不需要这些属性,因为它们很有可能指向一个特定的实例,而不是复用。

2.继承——借用构造函数

借用构造函数的方式并不能完全说是继承,就是借别人的方法来用下。

  function Person(name,age,sex){
            this.name = name;
            this.age = age;
            this.sex = sex;
        }
        function Student(name,age,sex,grande){
            Person.call(this,name,age,sex);
            this.grande = grande;
        }
        var student = new Student();

当我们要构造一个Student的构造函数,但是Person已经实现了我们部分功能,所以在Student中我们可以用Person中的方法就可以了。

借用构造函数弊端:

1.只能继承在构造函数中的方法,却不能继承那些添加到原型中的方法。

2. 这种继承每次要多调用一个函数,只是在视觉上省代码,实际运行上还浪费效率了。

3.共享原型

共享原型的方式简单粗暴,让一个原型同时给两个函数,从而达到继承的目的,如图:

看代码:

  Father.prototype.name = "长江七号" 
        function Father(){
            var age = 18;
        }
        function Son(){
            var sex = 'famle';
        }
        function inherit(Target,Origin){
            Target.prototype = Origin.prototype;
        }
        inherit(Son,Father)
        var son = new Son();
        var father = new Father();

尽管这两个对象共享了同一个原型,访问值的时候也很快,但是,这同时也是一个缺点,如果子对象修改了一个原型的属性,那么它会影响所有的祖对象。我们没有办法给子对象的原型单独给加属性。

属性修改过程:

以上三种继承方法多多少少都存在点缺点,接着引出第四种完美方法:

4.圣杯模式

圣杯模式其实跟共享原型的思路差不多,它是通过剪断父对象跟子对象的原型之间的直接关系,从而解决共享原型这一方法产生的问题,同时还可以继续共享原型上的属性,但改变子对象上原型的属性时,祖对象原型不受影响。如图所示:

看代码:

Father.prototype.name = "长江七号" 
        function Father(){
            var age = 18;
        }
        function Son(){
            var sex = 'famle';
        }
        function inherit(Target,Origin){
            function F(){};//中间空白函数代理
            F.prototype = Origin.prototype;
            Target.prototype = new F();
        }
        inherit(Son,Father)
        var son = new Son();
        var father = new Father();

⚠️注意:写圣杯模式的时候,我们一定要注意第10行和第11行,它们两个位置一定不能写反,必须在new之前改变原型指向,否则原型指向就指原来的自身原型上。**

接下来我们试试看好不好使:

ok!好使没问题,我们的目的达到了,几乎完美了,但是还差点,我们还需要重置构造函数指针,,如果不重置,那么所有子对象都会显示Father()是它们的构造函数,造成指向紊乱了,所以我们可以归下位:

Father.prototype.name = "长江七号" 
        function Father(){
            var age = 18;
        }
        function Son(){
            var sex = 'famle';
        }
        function inherit(Target,Origin){
            function F(){};
            F.prototype = Origin.prototype;
            Target.prototype = new F();
            Target.prototype.constructor = Target;//重置构造函数
        }
        inherit(Son,Father)
        var son = new Son();
        var father = new Father();

如果有一天我们想知道这个子对象真正继承自谁,在上面的基础上,我们还可以添加一个指向原始父对象的引用,加一个属性uber,本来是可以用"super"的,但是由于super是保留字的关键字。改进后如下代码:

Father.prototype.name = "长江七号" 
        function Father(){
            var age = 18;
        }
        function Son(){
            var sex = 'famle';
        }
        function inherit(Target,Origin){
            function F(){};
            F.prototype = Origin.prototype;
            Target.prototype = new F();
            Target.prototype.constructor = Target;//重置构造函数
            Target.prototype.uber = Origin.prototype;//访问超类,真正继承于谁
        }
        inherit(Son,Father)
        var son = new Son();
        var father = new Father();

其实还有一种写法,在雅虎的YUI库里面有个inherit方法:利用闭包的私有化属性

Father.prototype.name = "长江七号" 
        function Father(){
            var age = 18;
        }
        function Son(){
            var sex = 'famle';
        }
        // function inherit(Target,Origin){
        //     function F(){};
        //     F.prototype = Origin.prototype;
        //     Target.prototype = new F();
        //     Target.prototype.constructor = Target;
        // }
        var inherit = (function(){
            var F = function(){};
            return function (Target,Origin){
                F.prototype = Origin.prototype;
                Target.prototype = new F();
                Target.prototype.constructor = Target;
            }
        }());
        inherit(Son,Father)
        var son = new Son();
        var father = new Father();

『 好啦,以上呢就给大家的分享啦,如果您觉得本篇内容有帮助到你,可以转载但记得要关注,要标明原文哦,谢谢支持~』

「欢迎各位大佬关注我,扫二维码即可」

阅读 153

推荐阅读