<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                原文: [一篇文章理解 JS 繼承](https://mp.weixin.qq.com/s/Hjzt0DUd6aXIH84vrf0poQ) 說實在話,以前我只需要知道“寄生組合繼承”是最好的,有個祖傳代碼模版用就行。最近因為一些事情,幾個星期以來一直心心念念想整理出來。本文以《JavaScript高級程序設計》上的內容為骨架,補充了ES6 Class的相關內容,從我認為更容易理解的角度將繼承這件事敘述出來,希望大家能有所收獲。 ### 1. 繼承分類 先來個整體印象。如圖所示,JS中繼承可以按照是否使用object函數(在下文中會提到),將繼承分成兩部分(Object.create是ES5新增的方法,用來規范化這個函數)。 其中,原型鏈繼承和原型式繼承有一樣的優缺點,構造函數繼承與寄生式繼承也相互對應。寄生組合繼承基于Object.create, 同時優化了組合繼承,成為了完美的繼承方式。ES6 Class Extends的結果與寄生組合繼承基本一致,但是實現方案又略有不同。 下面馬上進入正題。 ![](https://box.kancloud.cn/5e7228c504efc5ddadf4b0e2e3aa1531_800x366.png) ### 2. 繼承方式 > 上圖上半區的原型鏈繼承,構造函數繼承,組合繼承,網上內容比較多,本文不作詳細描述,只指出重點。這里給出了我認為最容易理解的一篇[《JS中的繼承(上)》](https://segmentfault.com/a/1190000014476341)。如果對上半區的內容不熟悉,可以先看這篇文章,再回來繼續閱讀;如果已經比較熟悉,這部分可以快速略過。另,上半區大量借用了yq前端的一篇繼承文章1。 #### 2.1 原型式繼承 核心:將父類的實例作為子類的原型。 ~~~ SubType.prototype = new SuperType() // 所有涉及到原型鏈繼承的繼承方式都要修改子類構造函數的指向, // 否則子類實例的構造函數會指向SuperType。 SubType.prototype.constructor = SubType; ~~~ 優點:父類方法可以復用。 缺點: * 父類的引用屬性會被所有子類實例共享 * 子類構建實例時不能向父類傳遞參數 #### 2.2 構造函數繼承 核心:將父類構造函數的內容復制給了子類的構造函數。這是所有繼承中唯一一個不涉及到prototype的繼承。 `SuperType.call(SubType);` 優點: 和原型鏈繼承完全反過來 * 父類的引用屬性不會被共享 * 子類構建實例時可以向父類傳遞參數 缺點:父類的方法不能復用,子類實例的方法每次都是單獨創建的。 #### 2.3 組合繼承 核心:原型式繼承和構造函數繼承的組合,兼具了二者的優點。 ~~~ function SuperType() { this.name = 'parent'; this.arr = [1, 2, 3]; } SuperType.prototype.say = function() { console.log('this is parent') } function SubType() { SuperType.call(this) // 第二次調用SuperType } SubType.prototype = new SuperType() // 第一次調用SuperType ~~~ 優點: * 父類的方法可以被復用 * 父類的引用屬性不會被共享 * 子類構建實例時可以向父類傳遞參數 缺點:調用了兩次父類的構造函數,第一次給子類的原型添加了父類的name, arr屬性,第二次又給子類的構造函數添加了父類的name, arr屬性,從而覆蓋了子類原型中的同名參數。這種被覆蓋的情況造成了性能上的浪費。 #### 2.4 原型式繼承 核心:原型式繼承的object方法本質上是對參數對象的一個淺復制。 優點:父類方法可以復用。 缺點: * 父類的引用屬性會被所有子類實例共享 * 子類構建實例時不能向父類傳遞參數 ~~~ functionobject(o){ function F(){} F.prototype = o; return new F(); } var person = { name: "Nicholas", friends: ["Shelby", "Court", "Van"] }; var anotherPerson = object (person); anotherPerson.name = "Greg"; anotherPerson.friends.push("Rob"); var yetAnotherPerson = object(person); yetAnotherPerson.name = "Linda"; yetAnotherPerson.friends.push("Barbie"); alert(person.friends); //"Shelby,Court,Van,Rob,Barbie" ~~~ > ECMAScript 5 通過新增 Object.create()方法規范化了原型式繼承。這個方法接收兩個參數:一 個用作新對象原型的對象和(可選的)一個為新對象定義額外屬性的對象。在傳入一個參數的情況下, Object.create()與 object()方法的行為相同。——《JAVASCript高級編程》 所以上文中代碼可以轉變為: ~~~ var yetAnotherPerson = object(person); => var yetAnotherPerson = Object.create(person); ~~~ #### 2.5 寄生式繼承 核心:使用原型式繼承獲得一個目標對象的淺復制,然后增強這個淺復制的能力。 優缺點:僅提供一種思路,沒什么優點。 ~~~ function createAnother(original){ var clone = object(original); //通過調用函數創建一個新對象 clone.sayHi = function(){ //以某種方式來增強這個對象 alert("hi"); }; return clone; //返回這個對象 } var person = { name: "Nicholas", friends: ["Shelby", "Court", "Van"] }; var anotherPerson = createAnother(person); anotherPerson.sayHi(); //"hi" ~~~ #### 2.6 寄生組合繼承 剛才說到組合繼承有一個會兩次調用父類的構造函數造成浪費的缺點,寄生組合繼承就可以解決這個問題。 ~~~ function inheritPrototype(subType, superType){ var prototype = object (superType.prototype); // 創建了父類原型的淺復制 prototype.constructor = subType; // 修正原型的構造函數 subType.prototype = prototype; // 將子類的原型替換為這個原型 } function SuperType(name){ this.name = name; this.colors = ["red", "blue", "green"]; } SuperType.prototype.sayName = function(){ alert(this.name); }; function SubType(name, age){ SuperType.call(this, name); this.age = age; } // 核心:因為是對父類原型的復制,所以不包含父類的構造函數,也就不會調用兩次父類的構造函數造成浪費 inheritPrototype(SubType, SuperType); SubType.prototype.sayAge = function(){ alert(this.age); } ~~~ 優缺點:這是一種完美的繼承方式。 ### 2.7 ES6 Class extends 核心: ES6繼承的結果和寄生組合繼承相似,本質上,ES6繼承是一種語法糖。但是,寄生組合繼承是先創建子類實例this對象,然后再對其增強;而ES6先將父類實例對象的屬性和方法,加到this上面(所以必須先調用super方法),然后再用子類的構造函數修改this。 ~~~ class A {} class B extends A { constructor() { super(); } } ~~~ ES6實現繼承的具體原理: ~~~ class A {} class B {} Object.setPrototypeOf = function (obj, proto) { obj.__proto__ = proto; return obj; } // B 的實例繼承 A 的實例 Object.setPrototypeOf(B.prototype, A.prototype); // B 繼承 A 的靜態屬性 Object.setPrototypeOf(B, A); ~~~ ES6繼承與ES5繼承的異同: 相同點:本質上ES6繼承是ES5繼承的語法糖。 不同點: * ES6繼承中子類的構造函數的原型鏈指向父類的構造函數,ES5中使用的是構造函數復制,沒有原型鏈指向。 * ES6子類實例的構建,基于父類實例,ES5中不是。 ### 3. 總結 * ES6 Class extends是ES5繼承的語法糖 * JS的繼承除了構造函數繼承之外都基于原型鏈構建的 * 可以用寄生組合繼承實現ES6 Class extends,但是還是會有細微的差別 ### 參考文章: [《js繼承、構造函數繼承、原型鏈繼承、組合繼承、組合繼承優化、寄生組合繼承》](https://segmentfault.com/a/1190000015216289) 《JavaScript高級編程》
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看