JavaScript的继承

JavaScript常见的有四种继承方式:原型继承,构造函数继承,组合继承,class继承

1.原型继承

        原型继承就是通过改变原型链的方式来达到继承的效果

        语法: 子类.protype = 父类的实例

        function Person(name) {
            this.name = name
        }
        Person.prototype.sayHi = function () {
            console.log('hi')
        }

        // 实例化构造函数
        let p = new Person('派大星')
        // console.log(p)

        // 构造函数 Student 
        function Student() {
            this.age = 18
        }
        //在将父类实例写入子类的原型中
        Student.prototype = new Person('海绵宝宝')

        let s = new Student()
        console.log(s.name)
        console.log(s.sayHi)

 

        原型继承有一个缺点,父类的实例是写入到子类的原型里面的,student继承下来的属性并没有继承在student自己身上,而是在student的原型空间上有Person的name属性和sayHi()方法,不是写在我自己里面,我只能通过原型链去访问name属性和sayHi()方法。

       我继承的目的是为了继承属性和方法,但是我并没有继承到Person的属性,只继承到了sayHi方法。

2.构造函数继承

        在子类构造函数中使用父类构造函数,这种叫做构造函数继承,构造函数继承能够使子类函数可以继承到父类函数的属性和方法。

        function Person(name, gender) {
            console.log(this)
            this.name = name
            this.gender = gender
            this.sayHi = function () {
                console.log('hi')
            }
        }
        // 构造函数 Student (子类)
        function Student(age, name, gender) {
            this.age = age
            // 改变this指向
            Person.call(this, name, gender)

            // this.name = name
            // this.gender = gender
            // Person(name, gender)
            // 为什么继承失败了呢
            // 此时Person作为普通函数被调用,Person里面的this是window
            // name属性和gender属性都被加在了window上
            // 必须要调用父类的构造函数的时候,让父类的构造函数里面的this
            // 和子类里面的this指向的都是一样的地方,也就是指向实例对象
            // 如何改变本地调用的this指向呢

        }

        let s = new Student(18, 'lucy', '女')
        console.log(s)
        console.log(s.sayHi)

        在子类构造函数中,使用父类的构造函数,同时改变父类函数的this指向,因为在子类构造函数里,父类构造函数的普通函数的调用,this指向的是window,这样创建一个子类实例的话,Person函数的属性写到了window上了,要使用call或者apply函数临时改变Person方法的this指向,使其指向当前Student指向的实例,这样Person方法就只是一个普通的方法,在Student函数中执行,就可以使新创建的Student实例有Person的属性和方法了。

        顺便提一下new操作符的作用,在创建构造函数的时候,如果不使用new操作符,我们是不能返回一个实例对象的。new操作符帮我们实现了以下步骤:

// new 操作做了以下的一些事情
// 创建一个obj对象
// 将当前this指向obj对象
// 将this.prototype = 当前构造函数的原型

//这里是js执行我们自己定义的赋值操作

// 最后将obj返回

var obj = new Object
this = obj
this.prototype = Person.prototype

//这里是js执行我们自己定义的赋值操作

return obj

3.组合继承

        上面的构造函数继承也有一个缺点,每次创建一个Student实例我们都要重新创建一个sayHi()方法,这个方法明明一样,却要重复创建,浪费空间。如果数量多起来后,非常影响浏览器的性能。因此我们可以在Student的原型上使用原型继承,继承Person的方法,而使用构造函数继承的方式继承Person的属性,这就是组合继承。

        // 组合继承
        // 继承 =》两个构造函数之间的关系
        // 子类的实例使用父类的属性和方法

        // 父类
        function Person(name, age) {
            this.name = name
            this.age = age
        }
        Person.prototype.sayHi = function () {
            console.log('hi')
        }
        // 子类
        function Student(gender, name, age) {
            this.gender = gender
            // 构造函数继承
            Person.call(this, name, age)
        }

        // 原型继承
        Student.prototype = new Person()

        
        let s1 = new Student('女', '柳岩', 18)
        console.log(s1)
        console.log(s1.sayHi)
        // console.log(s1.age)
        // console.log(s1.name)

        // 注意
        // 如果子类的原型上也添加了方法,要在实现原型继承以后再添加
        // 否则,添加的sayHello会被继承的父类上的原型方法覆盖,sayHello就没意义了

4.es6的class继承

        ES6新加了一个新的构造函数定义方法

        在下面的代码中,constructor是class创建实例的时候执行的代码,通常在这里进行属性的定义和赋值,同时可以在创建实例的时候执行声明在class中方法,在class中声明的方法都是写在原型(prototype)中的,这样就不用我们声明式的使用   构造函数名.prototype = 方法 来在原型上定义方法,es6已经帮我们做好了。

        // es6 class语法
        // es6 有自己的书写类的语法 :class
        class Person {
            constructor(name, age) {
                this.name = name
                this.age = age
            }
            sayHi() {
                console.log('hi')
            }
        }

        let p1 = new Person('海绵宝宝',18)
        console.log(p1)

         class的继承代码非常简洁,使用两个关键字,extends和super

        // es6 class语法
        // es6 有自己的书写类的语法 :class
        // es6 也有自己的继承关键字:extends 、super
        class Person {
            constructor(name, age) {
                this.name = name
                this.age = age
            }
            sayHi() {
                console.log('hi')
            }
        }

        class Student extends Person {
            constructor(gender, name, age) {
                // super关键字 就相当于 es5语法中 Person.call(this, name, age)
                // super就代表父类的构造函数,且他会自动改变this指向
                // super要写在子类构造函数的第一行
                super(name, age)
                this.gender = gender
            }
        }
        let s1 = new Student('女', '派大星', 18)
        console.log(s1)

   上面的Student继承Person的属性是在Student里面,可以访问和赋值,方法是自动帮我们写入到原型中,这样就即节省了代码,也不用在实例对象的时候重复创建函数空间,浪费内存空间,推荐使用。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值