写点什么

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

用户头像
法医
关注
发布于: 4 小时前
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();
复制代码


『 好啦,以上呢就给大家的分享啦,,谢谢支持~』

发布于: 4 小时前阅读数: 4
用户头像

法医

关注

公众号@前端猎手 2020.07.17 加入

喜欢用写作记录自己技术成长过程

评论

发布
暂无评论
JS完美收官之——继承发展史