js几种继承的方式

一、为什么要有JS继承

假如每一个实例对象,都要有自己的属性和方法的副本。这不仅无法做到数据共享,也是极大的资源浪费。(这就和我们现实中会继承父母的资产一样,你是可以选择不继承的,但是浪费钱啊)

二、ES5继承的三种方式

1.原型继承

function Person() {
        this.ages = [12,13]  //引用类型的属性
    }
    //父类原型对象上添加一个函数sayHi
    Person.prototype.sayHi = function () {
        console.log('hello world')
    }
    // 构建一个子类
    function Student() {
    }
    // 让 子类 继承 父类
    Student.prototype = new Person()
    var s1 = new Student()
    var s2 = new Student();
    s1.ages.push(24);        //修改一个实例对象的引用类型属性,所有实例对象的属性发生变化
    console.log(s1.ages);   //[12,13,24]
    console.log(s2.ages);   //[12,13,24]

原型链继承总结:子类.prototype = 父类的实例;
//原型链继承是改变了子类的原型指向(这就像我和爸爸之间的关系,我的原型链(proto)指向了我的爸爸,你要问我有什么,我把我所有的属性和方法展示给你看,然后我的身上还有一个 proto ,从那里可以查询到我爸爸有啥)
缺点:

  • 继承下来的属性并没有在该子类身上,而是在其 proto 上
  • 要用的属性和方法虽然被继承,但是在多个位置,不便于寻找
  • (不便于寻找)对书写,维护和阅读代码不太友好
  • 引用类型的属性被所有实例共享

2.构造函数继承
//前提:需要使用call函数改变函数内部 this 指向的方法

function Person(name) {
        this.name = "hello";
        this.ages = [12,13]  //引用类型的属性
    }
    //父类原型对象上添加一个函数sayHi
    Person.prototype.sayHi = function () {
        console.log('hello world')
    }
    // 构建一个子类
    function Student(age,name) {
        this.age = 18;
        Person.call(this,name);
    }
    var s1 = new Student()
    var s2 = new Student();
    s1.ages.push(24);
    console.log(s1.ages);   //[12,13,24]
    console.log(s2.ages);   //[12,13]
    console.log(s1.name);   //"hello"

构造函数继承总结:父类.call(this,属性,方法)
//构造函数继承方式是利用父类构造函数体, 向子类身上添加成员(我爸会钓鱼,我不会,所以我要继承他的钓鱼方法就要让他教我【爸爸.call(我,钓鱼)】)
优点:

  • 不用一个个在 proto 上找属性和方法了
  • 要用几个属性,在call函数里传参就行(你想学几个技能,就让你爸教给你几个)

缺点:

  • 只能继承父类的属性和方法,不能继承父类原型的属性和方法(因为你只找到你爸爸教你技能,所以你爷爷会啥你不清楚)
  • 父类的方法被继承,每new对象时都会有同样的函数空间出现,所以继承父类方法会造成资源浪费
    //因为构造函数继承支持多重继承,所以你要继承哪个父类,就 哪个父类.call(this,属性)(这意思就是你要向谁学习技能,就让他.call(你,技能))

3.组合继承
//组合继承:把原型链继承和构造函数继承融合在一起。

    // 1. 父类构造函数
    function Person(name, age) {
      this.name = name
      this.age = age
    }
    Person.prototype.sayHi = function () {
      console.log('hello world')
    }

    // 2. 子类构造函数
    function Student(gender, name, age) {
      this.gender = gender
      // 构造函数继承
      //   call 方法的参数
      //   第一个是要改变的this 指向
      //   从第二个参数开始依次给函数传递参数
      Person.call(this, name, age) // 这里的 this 就是 Student 的实例
      // 调用一个 Person 函数, 并且把 Person 里面的 this 改变成 Student 的实例
    }

    // 原型继承
    //   能继承属性和方法的
    //   s1 的 sayHi 方法是依靠这个 原型继承 继承下来的
    Student.prototype = new Person()
    
    var s1 = new Student('男', 'Jack', 18)
    console.log(s1)

组合继承总结:父类.call(this,属性,属性),再子类.prototype = 父类的实例
优点:

  • 将属性继承下来了,不用再一个个到_proto_找了(构造函数继承的优点)
  • 方法也继承下来了(原型链继承的优点)
    注意:组合继承方式的子类里面调用继承方式顺序不能调换,因为构建函数是根据参数决定继承哪些属性或方法,如果不写,等同于不继承

三、ES6继承的方式

es6的继承主要是class和extends来实现继承。

class Person{
        constructor(name) {
            this.name =name;
        }
        say(){
            console.log(`我的名字叫${this.name}`)
        }
    }
    class Student extends Person{
        constructor(name,age) {
            super(name);
            this.age = age;
        }
        get(){
            console.log(`我叫${this.name}今年${this.age}岁`);
        }
    }
    let p = new Student("123",26);
    p.say()    //我的名字叫123
    p.get();   //我叫123今年26岁

注:子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类没有自己的this对象,而是继承父类的this对象,然后对其进行加工,如果不调用super方法,子类就得不到this对象。因此,只有调用super之后,才可以使用this关键字。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值