一網打盡js繼承

繼承方式知多少

過完年,立刻又到了金三銀四的季節,這段時間不管是身經百戰的老鳥,仍是在程序界中殺個七進七出的高手,抑或是菜鳥都在這段時間內,瘋狂的惡補本身的知識面,以便於在跳槽的時候拿到一個趁心如意的offer —— 而js繼承沒必要說必然是一個很是高頻的基礎面試題面試

說它基礎是由於絕大部分都能說出幾點來,接下來我就帶你們全面的回顧一下這個知識點bash

JS是一種弱類型的面嚮對象語言(抽象,封裝,繼承,多態),因此在實現繼承上仍是一如既往的靈活函數

//父類
      function Person(name) {
           this.name = name || '無名'
           this.sayHello = function () {
               console.log('hello everyBody i am ' + this.name)
           }
       }
       Person.prototype.doWork = function (work) {
           console.log(this.name + '的工做是--' + work)
       }
複製代碼

ES5繼承

第一種 —— 原型繼承

原型繼承基本上是人人都會的功能無非就是 將父類的實例看成子類的原型, 其中牽扯到的知識點有兩個優化

  • this的指向ui

  • 原型的鏈條繼承this

//子類
       function Teacher() {}
       Teacher.prototype = new Person('張三')

       var ls = new Teacher() 
       console.log(ls.name)   // 張三
       ls.sayHello()          //hello everyBody i am 張三
       ls.doWork('教師')      //張三的工做是--教師 

       var zs = new Teacher()
       console.log(zs.name)   // 張三
       zs.sayHello()          //hello everyBody i am 張三
       zs.doWork('教師')     //張三的工做是--教師 

複製代碼

優勢:spa

  • 簡單,只要是父類原型上的方法和屬性均可以訪問使用

缺點:prototype

  • 優勢即缺點,多個子類實例共享父類的屬性
  • 實例不能傳參 即 var lisi = new Teacher('李四'),參數李四不會傳到父類,輸出依然是張三
  • 不能實現多繼承(多繼承是指繼承多個父類)

第二種 —— 經過構造函數繼承

經過call來調用父類構造函數,因此每個子類都會有一個父類實例的副本code

知識點對象

  • 經過call實現對象冒充
//子類
      function Teacher(name) {
           Person.call(this,name)
           this.name = name || '無名'
       }
       
       var zs = new Teacher('張三')
       console.log(zs.name)
       zs.sayHello()
       zs.doWork('教師')  //報錯

複製代碼

優勢

  • 經過call實現後,子類的實例拷貝的是父類的副本,不存在屬性共享的問題
  • 可實現多繼承,經過多個call調用

缺點

  • 優勢相似於拷貝賦值,因此實例化的對象只是子類的實例,而不是父類的實例
  • 沒法複用父類原型鏈上的方法

第三種 拷貝繼承

這個有點相似於上面,就是把父類的實例化遍歷,經過賦值的方式賦值給子類的原型

//子類
      function Teacher(name) {
           var example = new Person();
           for(var p in example){
               Teacher.prototype[p] = example[p];
           }
           Teacher.prototype.name = name || 'Tom';
       }
複製代碼

優勢

  • 可實現多繼承,即把多個父類的實例化經過遍歷的方式賦值給子類

缺點

  • 麻煩,由於要遍歷父類原型佔用內存高

第四種 —— 把父類的實例看成子類返回值

經過實例化父類,而後爲這個實例添加新的屬性

//子類
      function Teacher(name) {
           let example = new Person()
           example.name = name || '無名'
           return example
       }

       var zs = new Teacher('張三')
       console.log(zs.name)
       zs.sayHello()
       zs.doWork('教師')

複製代碼

優勢

  • 由於有return返回的值因此不論是new Teacher()仍是函數調用Teacher()返回的結果是同樣的

缺點

  • 由於返回的是父類的實例,因此實例化對象zs只是父類的實例,不是子類的實例
  • 不能實現多繼承

小結一下

上面的四種方法都各有本身的優缺點,可是整體來看要麼是不全面要麼是麻煩或者佔內存,通常在開發中不建議這樣使用,可是也根據你本身的實際狀況,好比上面的某個方式很符合你的業務場景,而又不考慮擴展之類的其它東西,也是可使用的

固然除了這些,js發展了這麼多年確定是有比上面四種更好的方法,咱們接着介紹

第五種 —— 組合繼承(構造 + 實例)繼承

經過call拷貝來父類的屬性給子類使用,而且經過原型鏈的方式實現原型鏈的繼承

//子類
      function Teacher(name) {
           Person.call(this,name)
           this.name = name || '無名'
       }
    
       Teacher.prototype = Object.create(Person.prototype)
       Teacher.prototype.constructor = Teacher

      var zs = new Teacher('張三')
       console.log(zs.name)
       zs.sayHello()
       zs.doWork('教師')
複製代碼

優勢

  • 解決了構造繼承和原型繼承的缺點

第六種 寄生組合式繼承

這種方式能夠理解爲第五種方法的優化

//子類
function Teacher(name) {
           Person.call(this,name)
           this.name = name || '無名'
       }
       (function(){
           let Example = function(){};
           Example.prototype = Person.prototype;
           Teacher.prototype = new Example();
           Teacher.prototype.constructor = Teacher
       })();


       var zs = new Teacher('張三')
       console.log(zs.name)
       zs.sayHello()
       zs.doWork('教師')
複製代碼

優勢

  • 堪稱完美

缺點

  • 有點複雜

ES6 繼承

class Person{
        constructor(name){
            this.name = name
        }
        sayName(){
            console.log('my name is ' + this.name)
        }
    }
    class Teacher extends Person{
        constructor(name){
            super()
            this.name = name
        }
    }
   console.log(new Teacher('zs'))
    new Teacher('zs').sayName()
複製代碼

子類必須在constructor方法中調用super方法,不然新建實例時會報錯。這是由於子類沒有本身的this對象,而是繼承父類的this對象,而後對其進行加工,若是不調用super方法,子類就得不到this對象。所以,只有調用super以後,纔可使用this關鍵字

底層實現:一個繼承語句同時存在兩條繼承鏈:一條實現屬性繼承,一條實現方法的繼承

class A extends B{}
A.__proto__ === B;  //繼承屬性
A.prototype.__proto__ == B.prototype;//繼承方法
複製代碼

從上面能夠看到不論是ES5仍是ES6繼承的基本實現方式是同樣的,我想這就是所謂的萬變不離其宗吧

若是有錯誤的地方 歡迎指正批評

相關文章
相關標籤/搜索