JS继承

作者: 写代码的海怪 | 来源:发表于2018-12-05 06:56 被阅读5次

    简介

    JS的继承对于Java的继承其实不太是一回事,JS的继承的原理就是用原型链。

    假设

    假设我们有两个类: Human 和 Man。Human有name属性和一个run方法;Man有namegender属性,还有一个fight方法。

    function Human(name) {
        this.name = name
    }
    Human.prototype.run = function() {}
    
    function Man(name) {
        this.name = name
        this.gender = '男'
    }
    Man.prototype.fight = function() {}
    

    现在开始我们的继承实现。

    ES5

    属性继承

    首先是属性的继承,其实对于Man来说,this.name = name是废话一话,因为如果继承了HumanHuman里面就有这句话,只不过两者的this不一样而已。但是我们可以通过call()来指定this就可以实现代码的复用。

    function Human(name) {
        this.name = name
    }
    Human.prototype.run = function() {}
    
    function Man(name) {
        // 调用Human构造函数
        // 实现 this.name = name
        Human.call(this, name)
        this.gender = '男'
    }
    Man.prototype.fight = function() {}
    

    方法继承

    除了属性的继承,我们还想要方法的继承,一般来说方法的继承是指要继承Humanprototype的方法,而不是自有方法。简单来说就是new Man()出来的对象可以用Human.prototype里的方法。

    根据原理链的查找,我们可以得出下面的的代码

    function Human(name) {
        this.name = name
    }
    Human.prototype.run = function() {}
    
    function Man(name) {
        Human.call(this, name)
        this.gender = '男'
    }
    
    // 连接了Human.prototype,相当于继承了里面的方法
    Man.prototype.__proto__ = Human.prototype
    Man.prototype.fight = function() {}
    

    这样new Man()出来的对象就可以用run()方法了。

    IE

    但是像这种直接操作__proto__在IE里是不被允许的,我们得另找方法。参考如下代码。

    var arr = new Array()
    arr.__proto__ === Array.prototype // true
    

    根据上面的等式,我们可以推断出

    Man.prototype = new Human()
    

    这样使用代入法,不就是我们想要的吗?

    Man.prototype.__proto__ === Human.prototype // true
    

    但是这里有个问题,直接new Human()会执行里面Human函数的代码,这样不好,所以我们可以用一个临时变量来做。

    var f = function(){}
    f.prototype = Human.prototype
    Man.prototype = new f()
    
    • 我先把prototype放到临时函数的prototype
    • 再去用这个临时函数构造一个对象,这样不就可以不执行Human的代码了么,搞定!

    终极爆炸ES5继承代码

    function Human(name) {
        this.name = name
    }
    Human.prototype.run = function() {}
    
    function Man(name) {
        // 属性继承
        Human.call(this, name)
        this.gender = '男'
    }
    var f = function(){}
    // 继承Human 方法
    f.prototype = Human.prototype
    Man.prototype = new f()
    
    Man.prototype.fight = function() {}
    

    ES6

    有了ES6就爽很多了,ES6提供了一个语法糖

    class Human {
        constructor(name) {
            this.name = name
        }
    
        run() { }
    }
    
    // extends 相当于方法的继承
    // 替换了上面的3行代码
    class Man extends Human {
        constructor(name) {
            // super 相当于属性的继承
            // 替换了 Human.call(this, name)
            super(name)
            this.gender = '男'
        }
    
        fight() { }
    }
    

    但是ES6也不是万能的,如果Human.prototype中想写入一个非函数变量如a: 1ES6就不行了。ES5可以直接操作Human.prototype.a = 1就可以了,但是ES6语法只能写函数。

    不过我们有一个变通的方法。

    function Human(name) {
        this.name = name
    }
    Human.prototype.run = function() {}
    
    function Man(name) {
        Human.call(this, name)
        this.gender = '男'
    }
    var f = function(){}
    f.prototype = Human.prototype
    Man.prototype = new f()
    Man.prototype.fight = function() {}
    
    class Human {
        constructor(name) {
            this.name = name
        }
        // 这里的函数会被继承,所以可以获取1值
        getA() {
            return 1
        }
    
        run() { }
    }
    
    // extends 相当于方法的继承
    // 替换了上面的3行代码
    class Man extends Human {
        constructor(name) {
            // super 相当于属性的继承
            // 替换了 Human.call(this, name)
            super(name)
            this.gender = '男'
        }
    
        fight() { }
    }
    

    总结

    • 其实JS的继承就是
      • 属性继承:绑定this,调用父类构造器
      • 方法继承:子.__proto__ = 父.prototype,使用原理链完成串连
    • ES5方法麻烦,但是能够让我们看清JS继承的本质
    • ES5方法简单,但是由于不能直接操作prototype,所以没有ES5方法灵活

    相关文章

      网友评论

        本文标题:JS继承

        本文链接:https://www.haomeiwen.com/subject/qvgbcqtx.html