<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          公有屬性繼承的7種方式總結(繼承系列-4)

          共 6611字,需瀏覽 14分鐘

           ·

          2021-10-12 11:56


          繼承的幾種方式分析

          用自己的理解,系統(tǒng)整理下以上幾種繼承,從簡至更簡更高級??偨Y如下:

          • 前言:

          • 1、共享原型的繼承方式

          • 2、原型鏈繼承的方式

          • 3、圣杯前身

          • 4、圣杯模式的繼承

          • 5、高級模式:變廢為寶

          • 6、es6中的繼承 - setPrototypeOf

            • 仿寫實現(xiàn):

            • 擴展:Object.getPrototypeOf(b.prototype)

          • 7、Object.create()

          • 公有屬性繼承的7種方式 - 關鍵代碼總結

          • 總結一下

          • 最后驗證代碼

          • 思考


          前言:

          接下來簡寫字母表示的含義如下:

          • A表示下例代碼中的Person構造函數(shù),
          • A實例化對象a = new Person() = person,
          • B表示下例代碼中的Man構造函數(shù)
          • B實例化對象b = new Man() = man,
          • 中轉Temp函數(shù)不變
          /* 構造函數(shù) */
          function Person(name{
           this.name = name; //私有屬性
           return this;
          }
          Person.prototype.eat = function (//公有屬性/方法
           console.log('I can eat');
           return this;
          }
          var person = new Person('構造函數(shù)');
          console.log(person);
          function Man(sex{
           Person.call(this'繼承私有屬性'); //借用構造函數(shù)的方式實現(xiàn)其他對象私有屬性的繼承。
           this.sex = sex;
          }

          1、共享原型的繼承方式

          比如讓B.prototype = A.prototype
          這么做的缺點是大家成了綁在一條繩上的螞蚱,一損俱損、一改都改。
          且B構造出來的實例化對象的constructor成了A,而不是B了。這就亂套了,瞎認祖宗了。

          2、原型鏈繼承的方式

          比如讓B.prototype = new A()。
          實現(xiàn)原理是原型指向構造函數(shù)的實例化對象。通過實例化對象的__proto__去得到構造函數(shù)的原型。
          缺點也是B構造出來的實例化對象的constructor成了A,而不是B了。這就亂套了,認賊作父了。
          另外缺點還有A構造函數(shù)的實例化對象身上的私有屬性也被繼承了。

          所謂“認賊作父”:

          A的實例化對象的__proto__指向的就是A的原型,A原型上的constructor指向A本身這本沒有錯。

          但因為你把人家B的原型修改了,B的原型成了A的實例化對象,B構造出來的實例化對象b的__proto__就是A構造出來的實例化對象a,因此導致順著原型鏈的關系,A成了B實例化對象b的構造類。但實際b的構造類應該是B。

          3、圣杯前身

          為了解決以上問題,新方案是建一個第三方Temp函數(shù)中轉一下。

          讓 Temp.prototype = A.prototype;
          讓 B.prototype = new Temp();

          弊端:這樣還是改變不了B的實例化對象得到的constructor指向A這種認賊作父的局面。

          為什么指向不對?
          原因很簡單:B的實例化對象(b)沿著__proto__查找constructor。
          b.__proto__ == B.prototype = new Temp(),而new Temp得到的是個實例化對象,普通對象身上沒有constructor,只有原型對象身上有。
          所以還得沿著原型鏈找到Temp實例化對象的__proto__,即Temp.prototype,而Temp.prototype == A.prototype,這次終于找到了prototype原型對象、也找到了constructor。但是遺憾的是,這個prototype是A的,A.prototype.constructor === A;,所以最后結果還得是A。

          4、圣杯模式的繼承

          沒辦法了,我們知道constructor是誰,但是JS引擎搞混了。
          所以,只能在上一個基礎上,人為的去修改constructor,并將這塊代碼封裝起來,得到終極的封裝代碼“圣杯模式”:

          (function ({
            var Inherit = function ({};
            Inherit.prototype = Person.prototype;
            /* 上一句可以優(yōu)化,可以利用Object.create簡化這一步。改成:
              var protoType = Object.create(Person.prototype);
              Man.prototype = protoType; 
              這樣寫, 也就是下邊第7條的意思
            */

            Man.prototype = new Inherit();
            Man.prototype.constructor = Man; //在prototype添加固定屬性,中途攔截一下constructor
            Man.prototype.uber = Person.prototype; // 設置他的超類
          }());

          5、高級模式:變廢為寶

          prototype.__proto__指向的是Object.prototype,但Object原型上的內容我們基本不會用、且每個對象原型鏈的最后都是Object的原型,即使被改了也能最終找到Object原型。那既然這樣,為何不從Object的原型下手改一下?

          B原型上實例化對象指向A原型。Man.prototype.__proto__ = Person.prototype;這堪比將黃河改道?。『喼辈灰呒?。直呼內行!

          冷靜一下啊,缺點還是有的,就是__proto__作為隱式屬性,是系統(tǒng)自己的屬性,不建議我們去修改,如果理解不透徹容易改錯,所以不推薦使用。

          這么好的思路不讓用豈不是可惜?

          不必悲傷,因為官方內部利用這個原理幫我們實現(xiàn)了:

          6、es6中的繼承 - setPrototypeOf

          該方法就是讓B的原型指向A的原型。

          代碼如下

          Object.setPrototypeOf(B.prototype, A.prototype)

          其原理同第五條,只不過是es6給我們新增的官方用法。更安全、更可靠。

          仿寫實現(xiàn):

          Object.setPrototypeOf = function(_pro, proto){
           _pro.__proto__ = proto;
           return _pro;
          }

          擴展:Object.getPrototypeOf(b.prototype)

          MDN:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getPrototypeOf

          7、Object.create()

          有了上一個Object的方法,聯(lián)想到另一個es5的Object.create()。

          本意用來創(chuàng)建一個新對象,第一個參數(shù)是一個對象,用來當做新對象的__proto__,第二個參數(shù)是一個配置對象,和Object.defineProperties()的第二個參數(shù)一致。

          因此,我們修改原型指向也可以用這個方法創(chuàng)建出來的對象:

          Man.prototype = Object.create(Person.prototype, {
           constructor: {
            value: Man
           }
          });

          公有屬性繼承的7種方式 - 關鍵代碼總結

          1、原型直接指向原型,簡單粗暴

          Man.prototype = Person.prototype;

          2、原型指向實例化對象

          Man.prototype = new Person('繼承第二種');

          3、中轉函數(shù),粗糙圣杯

          function Temp(){};
          Temp.prototype = Person.prototype;
          Man.prototype = new Temp();

          4、精美圣杯模式

          (function(){
           var Inherit = function(){};
           Inherit.prototype = Person.prototype;
           Man.prototype = new Inherit();
           Man.prototype.constructor = Man;// 在prototype添加固定屬性,中途攔截一下constructor
           Man.prototype.uber = Person.prototype; // 設置他的超類
          }());

          5、高級模式:直接改變原型上的隱式原型

          Man.prototype.__proto__ = Person.prototype;

          6、Object.setPrototypeOf(B.prototype, A.prototype);

          Object.setPrototypeOf(Man.prototype,Person.prototype); // 第五條原理的官方實現(xiàn)

          7、Object.create(A.prototype,{…})

          Man.prototype = Object.create(Person.prototype, { // class中的繼承原理寫法
           constructor: {
            value: Man
           }
          });

          總結一下

          從第五條開始,這個思路高明的地方所在:
          這么做也很合理,因為man.__proto__指向Man.prototype,還有自己的使命不能被直接替換,但是Man.prototype.__proto__作為Object.Prototype貌似除了要用Object定義的方法外沒啥作用,所以從這一骨節(jié)上嫁接一下,把Person.prototype按到這里,幸運的是,Person.prototype.__proto__也有Object.prototype,所以我們不僅賺了夫人,也沒賠了兵。

          最后驗證代碼

          分別用上邊的方案實現(xiàn)繼承后,可以用下邊的代碼驗證下效果。

          var man = new Man('male');
          console.log(man);
          console.log(man.constructor);

          思考

          這些方法其實都很麻煩,為什么還要人為的去搞一下繼承?
          好像要科學方法改細胞似的。就不能天生繼承嗎?
          這就是class出現(xiàn)的原因了。
          class寫法更簡單,目的更明確。
          具體寫法和做法,可以看后續(xù)es6 - 《class》篇章


          愿你歷盡千帆,歸來仍是少年。


          讓我們一起攜手同走前端路!

          關注公眾號回復【加群】即可

          ● 工作中常見頁面布局的n種實現(xiàn)方法

          ● 三欄響應式布局(左右固寬中間自適應)的5種方法

          ● 兩欄自適應布局的n種實現(xiàn)方法匯總

          ● 工作中常見的兩欄布局案例及分析

          ● 垂直居中布局的一百種實現(xiàn)方式

          ● 常用九宮格布局的幾大方法匯總

          ● 為什么操作DOM會影響WEB應用的性能?

          ● 移動端滾動穿透的6種解決方案

          ● Vue + TypeScript 踩坑總結

          瀏覽 26
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  伊人影院av | www.91久久爱 | 蜜桃在线视频人妻 | 日逼的网站 | 亚洲天堂女人 |