likes
comments
collection
share

JS的继承的方式有哪些?优缺点是什么?

作者站长头像
站长
· 阅读数 9

在很多资料中,传统的JS继承方式有七种分别是:

  • 原型链继承
  • 借用构造函数继承
  • 组合继承
  • 原型式继承
  • 寄生式继承
  • 寄生式组合继承
  • ES6的class继承

此外,在文章的最后再多介绍一种继承方式 ——— 拷贝继承。

1、原型链继承

思路:本质是改造原型链

子构造函数.prototype = new 父构造函数()

// 1.定义Person构造函数
function Person (name,age){
  this.name = name
  this.age = age
}
Person.prototype.say = function(){
  console.log("人类会说话")
}
// 2.定义Student构造函数
function Student(name,age,className){
  this.name = name
  this.age = age
  this.className = className
}
// 3.语法:子构造函数.prototype = new 父构造函数()
// 让Student的原型指向父级构造函数new出来的实例对象,从而可以向上访问到Person的原型方法
Student.prototype = new Person()
// 4.修改子类的 constructor 依旧指向子类的构造函数
Student.prototype.constructor = Student
Student.prototype.study = function(){
  console.log("学生在学习")
}
let stu = new Student("张三",18,"80期")
console.log(stu)
JS的继承的方式有哪些?优缺点是什么?

原型链继承的优缺点

  • 优点1:代码实现比较简单
  • 缺点1:在创建子类实例的时候不能向父类构造函数传递参数
  • 缺点2:修改其中一个子类实例对象,会影响其它子类实例对象引用值

2、借用构造函数

思路:在子类构造函数中重新调用父类构造函数进行传参

父构造函数.call(this,...agu)

function Person(name,thing){
  this.name = name
  this.thing = thing
  // 实例对象上的方法,意味着每创建一个实例,将会创建一个该方法,占用内存大,性能不佳
  this.study = function() {
      console.log("好好学习")
  }
}
Person.prototype.sayHi = function() {
  console.log("子类实例不能获取到父类原型上的方法")
}

function Student(name,thing){
  Person.call(this,name,thing)
  //call意思是使用this对象指向到Person对象,Student可以直接调用Person所有属性以及方法
}

Student.prototype.addThing = function (item){
  this.thing = [item]
}

let stu1 = new Student('小明',['书包'])
console.log('stu1',stu1)

let stu2 = new Student()
stu2.addThing('笔')
console.log('stu2',stu2)

let stu3 = new Student()
console.log('stu3',stu3)
JS的继承的方式有哪些?优缺点是什么?

借用构造函数继承的优缺点

  • 优点:在创建子类实例的时候能向父类构造函数传递参数
  • 缺点:子类不能获取父类原型对象上的方法(但是可以获取父类实例对象上的方法),也就是无法实现函数方法的复用。

3、组合继承(借用构造函数+原型链继承)

思路:通过借用构造函数,将子类的this指向父类并给它传值。又通过原型链继承,子类的实例对象可以调用父类的原型方法。

父构造函数.call(this,...agu) + 子构造函数.prototype = new 父构造函数()

function Person(name,thing){
  this.name = name
  if(thing){
    this.thing = [thing]
  }else{
    this.thing = thing
  }
}

function student(name,thing){
  Person.call(this,name,thing)
}

Person.prototype.addThing = function(item){
  if(this.thing){
    this.thing.push(item)
  }else{
    this.thing = [item]
  }
}

student.prototype = new Person()

let stu1 = new student('小红','笔')
console.log('stu1',stu1)

let stu2 = new student()
stu2.addThing('书包')
console.log('stu2',stu2)

let stu3 = new student()
console.log('stu3',stu3)
JS的继承的方式有哪些?优缺点是什么?

组合继承的优缺点

  • 优点:每个子类生成的实例互相独立不影响,且可以传值给父类或使用父类的方法。
  • 缺点1:调用了两次父类的构造函数
  • 缺点2:由于我们是以父类的实例来作为子类的原型,造成了子类的原型中多了很多不必要的属性

4、原型式继承

思路:基于已有的对象来创建新的对象,原型式继承是通过使用一个临时构造函数和Object.create()方法来实现继承,向函数中传入一个对象,然后返回一个以这个对象为原型的对象。

特点:这种继承的思路主要不是为了实现创造一种新的类型,只是对某个对象实现一种简单继承。

Object.create(父对象)

// Object.create()底层实现原理 (ES6的新特性)
Object.create =  function (o) {
  var F = function () {}; // 创建一个临时构造函数
  F.prototype = o;  // //将传入的对象赋值给临时构造函数的原型
  return new F(); // 返回这个临时构造函数的一个实例,object()是对传入的对象执行了一次浅复制
};
//-------start--------

let person = {
  name:'人',
  things:[],
  sayHi: function() {
    console.log('Hi')
  }
}


let stu1 = Object.create(person)
stu1.name = '小红'
stu1.things.push('笔')
console.log('stu1',stu1)

let stu2 = Object.create(person)
stu2.name = '小明'
console.log('stu2',stu2)

stu1.sayHi()
JS的继承的方式有哪些?优缺点是什么?

 原型式继承的优缺点

  • 优点:不需要定义子类构造函数,以及原型链.prototype的指向操作
  • 缺点1:类似于复制一个对象,用函数来包装,不能给构造函数传递参数
  • 缺点2:浅拷贝,引用类型的值会被所有实例共享

5、寄生式继承

思路:创建一个用于封装继承过程的函数,通过传入一个对象,然后复制一个对象的副本,然后对象进行扩展,最后返回这个对象。这个扩展的过程就可以理解是一种继承。

// 定义一个空函数,其原型指向父对象
function object(o){
  function F(){}
  F.prototype = o
  return new F()
}

function createAnother(obj){
  // 从object方法中拿到一个原型指向obj的空函数对象
  let clone = object(obj)
  // 完善子函数的属性(增强)
  clone.sayHi = function() {
    console.log('Hi ' + this.name);
  };
  // 返回子函数
  return clone
}

let person = {
  name:'小明',
  age: 18,
  hobby: ['唱','跳']
}

//生成一个新的原型对象stu1,并拥有了person的属性以及方法。(其中包括方法sayHi())
let stu1 = createAnother(person)
stu1.hobby.push('rap')
stu1.sayHi()
console.log('stu1',stu1)

let stu2 = createAnother(person)
console.log('stu2',stu2)
JS的继承的方式有哪些?优缺点是什么?

寄生式继承的优缺点

  • 优点:不需要单独创建构造函数
  • 缺点1:浅拷贝,引用类型的值会被所有实例共享
  • 缺点2:借用构造函数继承类似,调用一次函数就得创建一遍方法,无法实现函数复用,效率较低

6、寄生式组合继承

利用组合继承和寄生继承各自优势 组合继承方法我们已经说了,它的缺点是两次调用父级构造函数,一次是在创建子级原型的时候,另一次是在子级构造函数内部,那么我们只需要优化这个问题就行了,即减少一次调用父级构造函数,正好利用寄生继承的特性,继承父级构造函数的原型来创建子级原型。

思路:不必为了指定子类型的原型而调用父类型的构造函数,我们需要是父类型原型的一个副本。本质上,就是使用寄生式继承来继承父类型的原型,然后再将结果指定给子类型的原型。

//封装一个函数inherProto,两个参数,参数1为子级构造函数,参数2为父级构造函数
function inherProto(son, parent) {
    //利用Object.create(),将父级构造函数原型克隆为副本clone 
    var clone = Object.create(parent.prototype); //创建对象
    //将该副本作为子级构造函数的原型
    son.prototype = clone; //指定对象
    //给该副本添加constructor属性,因为上一行中修改原型导致副本失去默认的属性
    clone.constructor = son; //增强对象
}

function Parent(name) {
    this.name = name;
    this.thing = ['笔', '尺子', '水杯'];
}
Parent.prototype.sayHi = function() {
    console.log('Hi'+ this.name);
}

function Son(name) {
    Parent.call(this, name);
}

inherProto(Son, Parent);

son1 = new Son('小明');
son1.thing.push('书包');
son1.sayHi(); 
console.log('son1',son1)

son2 = new Son('小红');
son2.thing.push('橡皮擦');
son2.sayHi(); 
console.log('son2',son2)
JS的继承的方式有哪些?优缺点是什么?

寄生式组合继承的优缺点

  • 优点:设置继承关系时只调用一次,节省空间
  • 缺点:封装继承关系时,代码比较繁琐难以理解

7、ES6 的 Class继承

ES6 的class可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,class写法只是为了让对象原型的写法更加清晰、更像面向对象编程的语法而已。

// ES6的继承:
//     1、继承关系的关键字:extends;
//     2、子类构造函数里必须调用super(),而且必须写在子类构造函数的第一句话
//     3、ES6的这种写法就是个语法糖(换了写法,本质一样)

class Person {
    constructor(name, sex) {
        this.name = name;
        this.sex = sex;
    }
    eat(str) {
        console.log(this.name + "在吃" + str);
    }
}
// extends关键字完成继承关系
class Student extends Person {
    constructor(name, sex, age) {
        // super就是父类
        super(name, sex); //这就相当于调用了父类的构造函数。这句话必须放在子类构造函数里的第一句话
        this.age = age;
    }
}
let stu1 = new Student("小明", "男", 6);
stu1.eat("面包");
console.log('stu1',stu1);

let stu2 = new Student("小红", "女", 5);
stu2.eat("苹果");
console.log('stu2',stu2);
JS的继承的方式有哪些?优缺点是什么?

Class继承的优缺点

  • 优点:极大地简化了原型链代码
  • 缺点:不能兼容所有的浏览器

8、拷贝继承

思路:利用 for... in... ,把父类实例的所有内容, 遍历一份到子类的原型上

function Person(age) {
  this.action = ["吃饭","睡觉"]
  this.age = age
  this.study = function() {
    console.log(this.name + "需要好好学习")
  }
}
Person.prototype.run = function() {
  console.log(this.name + '正在跑步,年龄是:' + this.age)
}

function Student(name,age) {
  let person = new Person(age);
  for(let key in person) {
    if(person.hasOwnProperty(key)) {
      this[key] = person[key]
    }else{
      Student.prototype[key] = person[key]
    }
  }
  this.name = name
}

let stu = new Student("小明",12);
stu.study()
stu.run()
console.log('stu',stu)
JS的继承的方式有哪些?优缺点是什么?

拷贝继承的优缺点

  • 优点:支持多继承
  • 缺点1:效率较低,内存占用高(因为要拷贝父类的属性)
  • 缺点2:无法获取父类不可枚举的方法(不可枚举方法,不能使用for in 访问到)
转载自:https://juejin.cn/post/7352075763834470438
评论
请登录