继承

107 阅读6分钟

继承

    function Person() {
            this.name = 'QF001'
        }
        Person.prototype.abc = function () {
            console.log('QF002')
        }

        function Stu() {
            this.age = 18
        }
        const s1 = new Stu()
        console.log('实际的对象: ', s1)

        const obj = {
            name: 'QF001',
            age: 18,
            abc: function () {console.log('QF002')}
        }
        console.log('期望得到的对象', obj)

目前我们创建的 Stu 的实例化对象中,只有一个 age 属性

但是如果说 我想拥有 Person 构造函数内 属性, 以及他原型上的方法

那么我们就需要利用继承解决上述问题

如果我们真的得到了期待对象, 那么一定是 Stu 继承了 Person 内的属性和原型上的方法

此时 我们可以说 Stu 是 Person 的子类

  Person 是 Stu 的父类

原型继承

在 ES6 正式推出 class 类以及对应的继承之前

我们的开发中某些时候, 可能需要用到继承, 那么此时需要有一种继承的方式就是利用了 原型

所以我们将这个方法称之为 原型继承

这种继承方式是将 自己的原型对象更改为 要继承的 父类的 实例化对象

  • 缺点:
    • 自己的原型对象消失了
    // 父类
    function Person() {
        this.name = 'QF001'
    }
    Person.prototype.abc = function() {
        console.log('QF002')
    }
    
    // 子类
    function Stu() {
        this.age = 18
    }
    
    // 将自身的原型对象, 更改为 要继承的 父类的实例化对象
    Stu.prototype = new Person()
    
    // 注意: 原型继承后, 如果需要再 原型上添加方法, 一定要在原型继承后添加, 不要在原型继承前添加
    Stu.prototype.qwe = function() {console.log()}
    
    const s1 = new Stu()
    console.log('实际的对象: ', s1)
    

借用继承

借用继承

  原型继承得到属性和方法都不在自已对象本身, 而是在在原型对象上

借用继承就是将属性继承到自己身上

这种继承方式 可以将父类所有属性继承到子类对象自身

但是父类的原型上的属性和方法, 跟子类没有任何关系

    // 父类
    function Person() {
        this.name = 'QF001'
        this.qwe = 'XF666'
    }
    Person.prototype.abc = function() {
        console.log('QF002')
    }
    
    // 子类
    function Stu() {
        this.age = 18
        Person.call(this)
    }
    
    cosnt s1 = new Stu()
    consoele.log('实际的对象: ', s1)

组合继承

  1. 原型继承: 能够继承到属性和原型上的方法, 但是继承到的属性不在对象本身
  2. 借用继承: 能够将父类的属性继承到对象本身, 但是父类原型上的方法继承不到

基于上述的两个方法, 我们将原型继承和借用继承组合起来, 创建了一个 组合 继承

  • 优点: 能够继承到所有的属性和原型上的方法, 并且属性能够继承到对象本身
  • 缺点: 在原型上会有一套多余的属性
    // 父类
    funtion Person(name, age){
        this.name = name
        this.age = age
    }
    Person.prototype.hihi= funciont(){
        console.log('hi')
    }
    // 子类
    funciotn Stu(photo){
        this.photo = phone
        
        // 借用构造函数继承
        Person.call(this,'张三',111
    }
    
    // 1. 原型继承
    Stu.prototype = new Person()
    
    cosnt s1 = new Stu(10086)
    console.log('实际的对象: ' , s1)

拷贝继承

    // 父类
    function Person(name, age) {
        this.name = name
        this.age = age
    }
    Person.prototype.hihi = function() {
        console.log('hi')
    }
    
    // 子类
    Stu 形参中的 ...
        将第二个位置开始的所有实参收集到一个数组中
    Person 实参中的 ...
        因为我函数需要两个参数, 而不是一个数组, 所以需要将数组展开
        
    function Stu(phone, ...args) {
        this.phone = phone
        const p = new Person(...args)
        for(let key in p) {
            
            // 将拿到的所有的内容 添加到 当前都早函数的原型对象中
            Stu.prototype[key] = p[key]
        }
    }
    
    const s1 = new Stu(10086, '张三', 18)
    console.log('实际的对象: ', s1)
       

ES6 继承

    // 父类 
    function Person(name, age) {
        this.name = name
        this.age = age
    }
    Person.prototype.hihi = function() {
        console.log('')
    }
    
   // 子类
       在实现 ES6 继承的时候, 书写方式有几个要求
       1. class 子类的类名 extends 要继承的父类类名 {}
       2. constructor 内部 必须书写 super
           他的作用可以在调用的时候对他进行传参, 传递的参数会传递到 父类中
       注意: 哪怕父类不需要书写任何参数, 我们的 super 也需要书写
           并且需要书写在 constructor 内部的一行 
       当前的方法只能给 class 类使用
           但是可以继承到构造函数中的内容, 也就是说父类是构造函数或者 class 都可以
   
   class Stu extends Person {
       constructor (id, ...args) {
           super(...args)
           this.id = id
       }
   }
   const s1 = new Stu(10086, '张三',18)
   console.log('实际的对象: ', s1 )

深浅拷贝

深浅拷贝 (只考虑引用数据类型, 当前案例中 只考虑 数组和对象)

  • 赋值

    • 因为引用数据在赋值的时候 是将地址传递过去了
    • 所以赋值后是将一个地址给到了一个新的变量, 如果你对以这个地址内做任何操作
    • 那么两个变量都会互相影响
  • 浅拷贝

    • 通过浅拷贝复制一个引用数据类型到另一个变量中
    • 其中这个数据内的基本数据类型修改的时候不会互相影响
    • 但是其中的 引用数据类型在修改的时候会互相影响
  • 深拷贝

    • 基于原本的数据, 复制出一个一模一样的, 但是内部数据的修改, 不会互相影响
深拷贝(工作版)
    const obj = {
        name: '我是对象的 obj',
        info: {
            width: 100,
            height: 101
        }
        arr: [100,200,300]
    }
    
    const newObj = JSON.parse(JSON.stringfy(obj))
    
    // 修改基本数据类型
    newObj.name = '新的名字'
    obj.name = '一个更新的名字'
    
    // 修改引用数据类型
    newObj.info.width = 999
    obj.arr[0] = 'QF001'
    
    console.log('原对象obj: ', obj)
    console.log('深层拷贝后的对象newObj: ', newObj)
深拷贝 (面试版)
    const obj = {
        name :'是对象 obj',
        info: {
            width :100,
            height: 101
        },
        arr: [100, 200, 300]
    }
    const newObj = {}
    function deepCopy(target, origin) {
        //target  目标对象           origin   原对象
        // 当函数执行完毕后, 会基于 origin 创建一个和他一模一样的对象, 放到 target 中
        
       for(let key in origin) {
           if(Object.prototype.toString.call(origin[key]) === '[object Object]'){
               // 说明当前的值为对象
               target[key] = {}
            deepCopy(target[key], origin[key])

                } else if (Object.prototype.toString.call(origin[key]) === '[object Array]') {
                    // 说明当前的值是 数组
                    target[key] = []
                    deepCopy(target[key], origin[key])

                } else {
                    // 说明当前的值是 基本数据类型
                    target[key] = origin[key]
                }
            }
        }
        deepCopy(newObj, obj)
        修改基本数据类型
        newObj.name = '新的名字'
        obj.name = '一个更新的名字'
        修改引用数据类型
        newObj.info.width = 999
        obj.arr[0] = 'QF001'
        console.log('原对象obj: ', obj)
        console.log('深拷贝后的对象newObj: ', newObj)
浅拷贝
       const obj = {
            name: 'QF001',
            age: 18,
            info: {
                width: 200,
                height: 300
            }
        }
        // 2.2 通过 对象内部的 一个方法 完成浅拷贝
        const newObj = Object.assign({}, obj)
        // 基本数据类型不会互相影响
        // obj.name = 'QF002'
        // newObj.age = 999
        // 引用数据类型 会互相影响
        obj.info.width = 666
        newObj.info.height = 777
        console.log('newObj', newObj)
        console.log('obj', obj)
        // 2.1 通过 for...in 完成浅拷贝
        const newObj = {}
        for (let key in obj) {
            const value = obj[key]
            newObj[key] = value
        }