1. 程式人生 > 其它 >javascript中的繼承

javascript中的繼承

技術標籤:jsjavascript繼承

javascript中的繼承

本章節摘自《javascript設計模式》人民郵電出版社中的繼承一節,感覺收穫很大,便自己作筆記整理一下,方便更好的理解

首先,繼承是什麼

繼承? 什麼是繼承呢,顧名思義,比如父母會把自己的一些特點遺傳給孩子,孩子具有了父母的一些特點,但又不完全一樣,總會有自己的特點, 但又不完全一樣,總會有自己的特點,所以父母和孩子都是獨立的個體,繼承可以使得子類具有父類的屬性和方法。 但是javascript中並不存在現有的機制,怎麼使用javascript實現繼承呢

javascript實現繼承該如何實現呢

  1. 第一種: 子類的原型物件–類式繼承

宣告父類, 當我們例項化一個父類的時候,新建立的物件複製了父類的建構函式內的屬性和方法,並將_proto_指向了父類的原型物件, 這樣既擁有了父類自己的屬性和方法,也擁有了父類原型的屬性與方法

        (function () {
          // subclass prototype --- > superClass 例項 ---> 訪問superClass 屬性與方法
          // subclass prototype._proto_ =  superClass.prototype
            function SuperClass(id)
{ this.books = ['javascript', 'html', 'css', 'vue', 'react'] this.SuperValue = true this.id = id } // 新增共有方法 SuperClass.prototype.getSuperValue = function () { return this.SuperValue } function
subClass(id) { this.SubValue = false this.subid = id } // 繼承父類, 將子類原型繼承於創建出來的父類的例項 subClass.prototype = new SuperClass(this.subid) subClass.prototype.getSubValue = function () { return this.SubValue } var instance = new subClass(1) var instance1 = new subClass(2) console.log(instance.SuperValue) console.log(instance.SubValue) // 缺點: instanceOf只可以檢測當前例項是否在某個類的原型鏈上, 無法判斷繼承,繼承並非例項, 這一點不能弄混 console.log(instance instanceof subClass) // true console.log(instance instanceof SuperClass) // true // 通過superclass 的例項 賦值給subclass.prototype console.log(subClass.prototype instanceof SuperClass) // true console.log(instance instanceof Object) // 建立的所有物件都是Object的例項 true console.log(subClass instanceof SuperClass) // false console.log(instance1.books) // ["javascript", "html", "css", "vue", "react"] console.log(instance) instance.books.push('設計模式') console.log(instance1.books) // ["javascript", "html", "css", "vue", "react", "設計模式"]

類式繼承的缺點
1: 由於子類通過其原型prototype對父類進行的例項化,繼承了父類,如果父類中的共有屬性為引用型別,如果子類修改了父類的屬性, 那麼,就會直接影響到其他子類
2: 由於子類實現的繼承是靠其原型prototype 對父類進行例項化實現的,因此在建立父類的時候,是無法向父類傳遞引數的,因而在建立父類的時候,也無法對父類建構函式的屬性進行初始化

  1. 建構函式繼承
        (function () {
            // superClass this指向 superClass
            function superClass(id) {
                this.books = ['javascript', 'html', 'css', 'vue', 'react'],
                this.id = id
            }
            superClass.prototype.showBooks = function () {
                console.log(this.books)
            }
            // subClass this指向 superClass
            function subClass(id) {
                
                // call方法可以改變函式的作用環境, 因此,在子類中,就是將子類的變數在父類中執行一遍
                superClass.call(this, id)
            }
            var instance1 = new subClass(10)
            var instance2 = new subClass(11)
            console.log(instance1.books) //  ["javascript", "html", "css", "vue", "react"]
            instance2.books.push('設計模式')
            console.log(instance2.books) //  ["javascript", "html", "css", "vue", "react", "設計模式"]
            console.log(instance1.books) //  ["javascript", "html", "css", "vue", "react"]
            console.log(instance2.id) // 11
            console.log(instance1.id) // 10
            console.log(instance1) 
            // 缺點:  只是通過改變this指向,拿到父類的屬性和方法,並沒有繼承父類的prototype
            // instance1.showBooks() // instance1.showBooks is not a function
        })()

建構函式繼承的缺點: 是通過改變this指向,拿到父類的屬性和方法,並沒有繼承父類的prototype,如果你想直接使用父類建構函式原型上的方法就會報錯,如instance1.showBooks is not a function

  1. 第三種繼承,組合繼承(集合前兩種繼承的優點,互補)
 (function () {
           function superClass(name) {
                this.books = ['javascript', 'html', 'css', 'vue', 'react'],
                this.name = name
            }
            superClass.prototype.showBooks = function () {
                console.log(this.books)
            }
            function subClass(name, time) {
                
                // call方法可以改變函式的作用環境, 因此,在子類中,就是將子類的變數在父類中執行一遍
                superClass.call(this, name)
                this.time = time
            }
            subClass.prototype = new superClass()
            subClass.prototype.getTime = function() {
                console.log(this.time)
            }

            var instance1 = new subClass('js book', 2014)
            // var instance2 = new subClass('js book', 2014)
            instance1.books.push('設計模式')
            console.log(instance1.books)
            // console.log(instance2.books)
            // instance2.showBooks()
        })()

組合繼承的缺點,父類建構函式會被執行兩次,在執行call方法時,父類建構函式會執行一次, 在將父類例項賦值給子類原型時,會再次執行一次

  1. 第四種繼承方案: 原型式繼承

概念: 宣告一個過渡函式物件, 相當於類式繼承中的子類,它是對類式繼承的封裝,只是對類式繼承的增強,但是並沒有解決類式繼承帶來的副作用

        (function(){
           function inheritObject(o) {
               // 宣告一個過渡函式物件, 相當於類式繼承中的子類
               // 相對於類式繼承的優點,由於F過渡類中的建構函式中沒有內容,開銷比較小,使用起來也比較方便,但是並沒有解決
               // 類式繼承的缺點
               function F() {}
               F.prototype = o
               // 返回過度物件中的例項,該例項的原型繼承了父物件
               return new F()
           }
           var book = {
              books: ['javascript', 'html', 'css', 'vue', 'react'],
              name: 'book1'
            }
            var newBook = inheritObject(book)
            newBook.name = 'book'
            newBook.books.push('設計模式')
            console.log(newBook.books) //  ["javascript", "html", "css", "vue", "react", "設計模式"]
            var newBook1 = inheritObject(book)
            newBook1.name = 'book1'
            newBook1.books.push('演算法')
            console.log(newBook.name) // book
            console.log(newBook1.name) // book1

            console.log(newBook1.books)//  ["javascript", "html", "css", "vue", "react", "設計模式", "演算法"]
        })()
  1. 第五種繼承方式, 寄生式繼承

寄生式繼承, 顧名思義,就像寄生蟲一樣寄託於某個物件內部生長, 是指依託於原型繼承, 並且可以建立自己的屬性和方法

        (function(){
           function inheritObject(o) {
               function F() {}
               F.prototype = o
               // 返回過度物件中的例項,該例項的原型繼承了父物件
               return new F()
           }
           var book = {
              books: ['javascript', 'html', 'css', 'vue', 'react'],
              name: 'book1'
            }
            function createBook(obj) {
                var o = inheritObject(obj)
                // 基於父物件拓展出的新物件
                o.getName = function() {
                    return this.name
                }
                return o
            }
            var newBook = createBook(book)
            newBook.name = 'book'
            newBook.books.push('設計模式')
            console.log(newBook.books) //  ["javascript", "html", "css", "vue", "react", "設計模式"]
            var newBook1 = createBook(book)
            newBook1.name = 'book1'
            newBook1.books.push('演算法')
            console.log(newBook.getName())

            console.log(newBook1.books)//  ["javascript", "html", "css", "vue", "react", "設計模式", "演算法"]
        })()
  1. 第六種繼承方式, 寄生組合繼承(終極繼承方式)
        (function () {
            function inheritObject(o) {
                function F() {}
                F.prototype = o
                // 返回過度物件中的例項,該例項的原型繼承了父物件
                return new F()
            }

            function inheritPrototype(subClass, superClass) {
                // 得到父物件的副本
                var p = inheritObject(superClass.prototype)
                p.constructor = subClass
                // 給子類的原型重新賦值為父物件的副本
                subClass.prototype = p

                // 通常副本通過原型繼承就可以得到,這裡為什麼不直接賦值呢,因為這麼賦值對父類原型物件複製得到的複製物件
                // p中的constructor指向的不是subClass的子類物件,因此在此繼承中要對複製物件p做一次增強,修復其constructor指向不正確的問題
                // 最後將得到的複製物件p賦值給子類的原型
            }

            function superClass(name) {
                this.name = name
                this.books = ['javascript', 'html', 'css', 'vue', 'react']
            }


            superClass.prototype.getName = function () {
                console.log(this.name)
            }

            function subClass(name, time) {
                superClass.call(this, name)
                this.time = time
            }
            inheritPrototype(subClass, superClass)
            subClass.prototype.getTime = function () {
                console.log(this.time)
            }
            var newBook1 = new subClass('book', 2014)
            var newBook2 = new subClass('book1', 2015)
            newBook1.books.push('設計模式')
            console.log(newBook1.books) //  ["javascript", "html", "css", "vue", "react", "設計模式"]
            console.log(newBook2.books) // ["javascript", "html", "css", "vue", "react"]
            newBook1.getTime() // 2014
        })()
  1. 第七種繼承方式,es6的class繼承

class 可以理解為function,class寫法只是讓物件原型的寫法更加清晰、更像面向物件程式設計的語法而已,也時基於prototype原型鏈查詢,當呼叫super時,相當於自動執行了call方法,更改子類的this指向,通過呼叫Object.create(),將子類_proto_指向父類

      class superClass {
        //建構函式,裡面寫上物件的屬性
        constructor(props) {
         this.books = ['javascript', 'html', 'css', 'vue', 'react']
        }
        //方法寫在後面
        getBooks = function () {
            return this.books
        }
      }

      //class繼承
      class subClass extends superClass {
        //建構函式
        constructor(props) {
          //props是繼承過來的屬性
          //呼叫實現父類的建構函式
          super(props) //相當於獲得父類的this指向
        }

        addBooks(book) {
          this.books.push(book)
        }
        getBooks() {
          return this.books
        }
      }
      var newBook1 = new subClass()
      var newBook2 = new subClass()
      newBook1.addBooks('設計模式')
      console.log(newBook1.getBooks()) // (6) ["javascript", "html", "css", "vue", "react", "設計模式"]
      console.log(newBook2.getBooks())// (5) ["javascript", "html", "css", "vue", "react"]