js继承的几种情况

继承在高级程序语言中是非常常用的,此处讨论js继承的几种情况

  1. 构造函数继承,使用call或apply
        let Father = function(){
            this.type = "person"
        }
        let Son = function(){
            this.age = 10
            Father.call(this);
        }
        let oneSon = new Son()
        console.log(oneSon.type)  //person
        console.log(oneSon.age)  //10

子实例内使用父类函数的call()使得子实例可以获取父类中的自有属性

  1. 原型继承
    先定义父类函数和子类函数
        let Father = function(){
            this.type = "person"
        }
        Father.prototype = {
            sex:"man",
            walk(){
                console.log("walk")
            }
        }
        Object.defineProperty(Father.prototype,"constructor",{
            value:Father,
            enumerable:false
        })  //由于将一个对象直接量赋值给父原型,故将其constructor属性定义回来并设置为不可枚举
        let Son = function(){
            this.age = 10
        }

一下为原型继承的各种情况
2.1 将父原型赋值给子原型,则改变子原型,父原型会跟着变,不符合需求

        Son.prototype = Father.prototype
        let oneSon = new Son()
        console.log(oneSon.type)  //undefined,此时没有继承自父构造函数
        console.log(oneSon.age)  //10
        console.log(oneSon.sex)  //man
        Son.prototype.sex = "women";   
        console.log(Father.prototype.sex)  //woman  改变子原型,父原型会跟着变
        console.log(oneSon.sex)  //woman
        oneSon.walk()  //walk

2.2 将父实例赋值给子原型,此时改变子原型则父原型不变,但是所有父内容都在子原型上

        Son.prototype = new Father()
        let oneSon = new Son()
        console.log(oneSon.type)  //person
        console.log(oneSon.age)  //10
        console.log(oneSon.sex)  //man
        Son.prototype.sex = "women";   
        console.log(Father.prototype.sex)  //man  改变子原型,父原型不变
        oneSon.walk()  //walk
        console.log(Object.keys(oneSon))  //打印子实例中可枚举的自有属性,结果为 ["age"]
        
        //尝试打印所有可枚举的自有属性以及继承属性,用for/in
        let arr = [];
        for(let i in oneSon){
            arr.push(i)
        }
        console.log(arr)  //["age","type","sex","walk"]

2.3 寄生式继承
2.3.1 赋实例

            let TempFunc = function(){};
            TempFunc.prototype = new Father();
            Son.prototype = new TempFunc()
            Object.defineProperty(Son.prototype,"constructor",{
            value:Son,
            enumerable:false
            })  //将子原型构造器矫正
            let oneSon = new Son()
            console.log(oneSon.type)  //person
            console.log(oneSon.age)  //10
            console.log(oneSon.sex)  //man
            oneSon.walk()  //walk
            console.log(Object.keys(oneSon))  //打印子实例中可枚举的自有属性,结果为 ["age"]
            
            //尝试打印所有可枚举的自有属性以及继承属性,用for/in
            let arr = [];
            for(let i in oneSon){
                arr.push(i)
            }
            console.log(arr)  //["age","type","sex","walk"]

此方式与父实例给子原型相似,且比前者多继承了一层,属实没有必要。
2.3.2 赋父原型

            let TempFunc = function(){};
            TempFunc.prototype = Father.prototype  //注意,此处是将父原型赋值给临时类的原型,
                                 //修改临时类的原型时父原型也会变,但我们此处没必要也不会这么做
            Son.prototype = new TempFunc()
            Object.defineProperty(Son.prototype,"constructor",{
            value:Son,
            enumerable:false
            })  //将子原型构造器矫正
            let oneSon = new Son()
            console.log(oneSon.type)  //undefined ,此时只继承了父原型,没有继承父构造函数
            console.log(oneSon.age)  //10
            console.log(oneSon.sex)  //man
            oneSon.walk()  //walk
            console.log(Object.keys(oneSon))  //打印子实例中可枚举的自有属性,结果为 ["age"]
            
            //尝试打印所有可枚举的自有属性以及继承属性,用for/in
            let arr = [];
            for(let i in oneSon){
                arr.push(i)
            }
            console.log(arr)  //["age","sex","walk"]

3.寄生组合式继承
结合1和2.3.2即可,下面是整合代码

            let inherit = function(Father,Son){
                if(Father&&typeof Father==="function"&&Son&&typeof Son==="function"){
                    //先寄生式继承原型
                    let TempFunc = function(){};
                    TempFunc.prototype = Father.prototype
                    Son.prototype = new TempFunc();
                    Object.defineProperty(Son.prototype,"constructor",{
                        value:Son,
                        enumerable:false
                    })  //将子原型构造器矫正

                    //再继承父构造函数,另一方面也是先改原型后创建实例,不然出
                //问题(原型对象赋值新对象时在它之前定义的实例不会及时变更)
                    let newSon = new Son();
                    Father.call(newSon);

                    //返回处理后的子实例
                    return newSon
                }else{
                    throw new Error("请传入两个构造函数")
                }
            }
            
          //使用inherit函数
            let exp = inherit(Father,Son);
            console.log(exp) 

    //输出:
            //Son {age: 10, type: "person"}
                // age: 10
                // type: "person"
                // __proto__: Father
                    // constructor: ƒ ()
                    // __proto__:
                        // sex: "man"
                        // walk: ƒ walk()
                        // constructor: ƒ ()
                        // __proto__: Object

总结:几种不同的继承方法的本质要搞明白,几种有明显缺点的继承方法就不要再用了,实际上关于js继承我们可以尽量用寄生组合式继承。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  •   面向对象(Object-Oriented,OO)的语言有一个标志,那就是它们都有类的概念,而通过类可以创建任意...
    霜天晓阅读 6,426评论 0 6
  • 继承的概念:子类可以使用父类共享的属性和方法,避免重复代码提高代码复用性。 原型链:子类可以共享父类的实例对象和实...
    浅秋_6672阅读 3,043评论 0 0
  • 博客内容:什么是面向对象为什么要面向对象面向对象编程的特性和原则理解对象属性创建对象继承 什么是面向对象 面向对象...
    _Dot912阅读 5,283评论 3 12
  • 1.继承(接口继承和实现继承) 继承是 OO 语言中的一个最为人津津乐道的概念。许多 OO 语言都支持两种继承方式...
    believedream阅读 4,535评论 0 3
  • 1、构造函数模式 [url=]file:///C:/Users/i037145/AppData/Local/Tem...
    横冲直撞666阅读 4,308评论 0 0