javascript中的继承

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"]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值