js对象:实现继承的几种方式及优缺点

18 篇文章 4 订阅

目录

一、原型链继承

二、构造函数继承

三、组合继承(构造函数式继承+原型链继承)

四、拷贝继承(原型式继承)

五、寄生式继承

六、寄生组合式继承

七、使用ES6中class关键字


一、原型链继承

子类通过prototype将所有在父类中通过prototype追加的属性和方法都追加到子类上

优点:

1、简单

2、父类新增方法、原型属性,子类都可以访问到

缺点:

1、所有新实例都会共享父类实例的属性(原型上的属性是共享的,一个实例修改了原型属性,另一个实例上的原型属性也会被修改)

2、子类无法给父类传递参数

3、无法实现多继承

<script>
  function Person(name) {
    this.name = name;
    this.say = function () {
      console.log(this.name);
    };
  }
  Person.prototype.hobby = '躺平'; // 原型属性

  function Student(age, major) {
    this.age = age;
    this.major = major
  }

  Student.prototype = new Person('小明'); // 将子类的原型指向person

  Person.prototype.eyes = '黑眼睛'; // 给父类添加原型属性

  let xiaoMing = new Student(10, '英语')
  console.log(xiaoMing.age, xiaoMing.major); // 10 "英语"
  console.log(xiaoMing.name); // 小明
  console.log(xiaoMing.eyes); // 黑眼睛
  console.log(xiaoMing instanceof Person); // true
</script>

二、构造函数继承

通过call 或 apply将父类构造函数引入子类构造函数(在子类函数中做了父类函数的自执行(复制))

优点:

1、解决了原型链继承方式的缺点

2、可以继承多个构造函数的属性(call多个)

3、在子实例中可向父实例传参

缺点:

1、只能继承父类构造函数的属性

2、无法实现构造函数的复用(每次都要重新调用)

3、每个新实例都有父类构造函数的副本

<script>
  function Person(name) {
    this.name = name;
    this.say = function () {
      console.log(this.name);
    };
  }
  Person.prototype.hobby = '躺平'; // 原型属性

  function Student(name, age, major) {
    // apply也可以,但apply的第二个参数为数组
    Person.call(this, name);
    this.age = age;
    this.major = major
  }
  let xiaoMing = new Student('小明', 18, '计算机科学与技术')
  console.log(xiaoMing.name); // 小明
  console.log(xiaoMing.age); // 18
  console.log(xiaoMing.major); // 计算机科学与技术
  console.log(xiaoMing.hobby); // undefined
  console.log(xiaoMing instanceof Person); // false
</script>

三、组合继承(构造函数式继承+原型链继承)

搭配使用构造函数和原型链

优点:

1、可继承父类原型上的属性

2、可传参

3、可复用

4、每个新实例的构造函数式私有的

缺点:

1、调用了两次父类的构造函数

<script>
  function Person(name) {
    this.name = name;
    this.say = function () {
      console.log(this.name);
    };
  }
  Person.prototype.hobby = '躺平'; // 原型属性

  function Student(name, age, major) {
    // apply也可以,但apply的第二个参数为数组
    Person.call(this, name);
    this.age = age;
    this.major = major
  }

  Student.prototype = new Person(); // 将student的原型对象指向person

  Person.prototype.eyes = '黑眼睛'; // 给父类添加原型属性

  let xiaoMing = new Student('小明', 18, '计算机科学与技术')
  console.log(xiaoMing.name); // 小明
  console.log(xiaoMing.age); // 18
  console.log(xiaoMing.major); // 计算机科学与技术
  console.log(xiaoMing.hobby); // 躺平
  console.log(xiaoMing.eyes); // 黑眼睛
  console.log(xiaoMing instanceof Person); // true
</script>

四、拷贝继承(原型式继承)

用一个函数包装一个对象,然后返回这个函数的调用,这个函数就变成了个可以随意增添属性的实例或对象,类似于Object.create()

优点:

1、支持多继承

缺点:

1、效率低

<script>
  function Person(name) {
    this.name = name;
    this.say = function () {
      console.log(this.name);
    };
  }
  Person.prototype.hobby = '躺平'; // 原型属性

  function inherit(p) {
    function F(){}; // 定义一个空构造函数
    F.prototype = p; // 将原型属性设置为p
    return new F();
  }

  let person = new Person('小明');
  let xiaoMing = inherit(person);
  xiaoMing.hobby = '打篮球';
  console.log(xiaoMing.name); // 小明
  console.log(xiaoMing.hobby); // 打篮球
</script>

五、寄生式继承

给拷贝继承外边再封装一层用于传递参数

<script>
  function Person(name) {
    this.name = name;
    this.say = function () {
      console.log(this.name);
    };
  }
  Person.prototype.hobby = '躺平'; // 原型属性

  // 拷贝继承
  function inherit(p) {
    function F(){}; // 定义一个空构造函数
    F.prototype = p; // 将原型属性设置为p
    return new F();
  }
  let person = new Person('小明');

  // 再包一层
  function studentObj(obj, age){
    let person = inherit(obj);
    person.age = age;
    return person;
  }
  let xiaoMing = studentObj(person, 18);
  console.log(xiaoMing.age); // 18
  console.log(xiaoMing.name); // 小明
  console.log(xiaoMing.hobby); // 躺平
  console.log(typeof xiaoMing); // object
</script>

六、寄生组合式继承

所谓寄生组合式继承,即通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。其背后的基本思路是:不必为了指定子类型的原型而调用超类型的构造函数,我们所需要的无非就是超类型原型的一个副本而已。本质上,就是使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型---《JavaScript高级程序设计》

<script>
  function Person(name) {
    this.name = name;
    this.say = function () {
      console.log(this.name);
    };
  }
  Person.prototype.hobby = '躺平'; // 原型属性

  // 定义继承方法
  function inherit(childType, parentType) {
    // 拷贝一份,相当于Object.create()
    function copyFun(o) {
      let Fun = function () { };
      Fun.prototype = o;
      return new Fun();
    }
    // 将childType的原型指向父类的原型的一个副本
    childType.prototype = copyFun(parentType.prototype);
    //强制constructor指向suberClass
    childType.prototype.constructor = childType;
  }

  // 子类
  function Student(name, age, major) {
    Person.call(this, name); //继承 Person 的属性, 此处是一份副本
    this.age = age;
    this.major = major;
  }
  inherit(Student, Person);
  Student.prototype.study = function () {
    console.log(`我在学${this.major}`);
  }
  let xiaoMing = new Student('小明', 18, '文史')
  console.log(xiaoMing);
  console.log(xiaoMing.age); // 18
  console.log(xiaoMing.major); // 文史
  console.log(xiaoMing.name); // 小明
  xiaoMing.study(); // 我在学文史
  
</script>

七、使用ES6中class关键字

挺好用的

<script>
  //使用class关键字构造父类Person
  class Person {
    constructor(name) {
      this.name = name;
    }
    say() {
      console.log(this.name)
    }
  }
  // 使用class和extends,super定义子类Student
  class Student extends Person {  // extends后边紧跟超类名称
    constructor(name) {
      super(name);   // 必须通过super调用,以获取父类中的所有方法和属性
      this.hobby = '游泳'
    }
  }
  let xiaoMing = new Student('小明');
  console.log(xiaoMing);
</script>

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值