js继承的几种方式

2019-05-28  本文已影响0人  纵横无涯

1 原型链继承

function Person () { // 父级构造函数
    this.name = 'zhangsan';
    this.age = 23
  }
  function Son () { // 子级构造函数
    this.num = 50
  }
  Son.prototype = new Person()

  Son.constructor = Son

  Son.prototype.count = 50
  
  var son = new Son()
  console.log(son.name, son.age, son.num)

把子类的prototype指向父级的实例,也就是原型链继承

此继承方法优

简单明了,父级新增的属性和方法子级都能够访问的到

此继承方法缺点

就是多个子类共享一个原型的属性和方法,无法实现多个继承

2,构造函数继承

// 构造函数继承
  // 构造函数继承
  function Person () {
    this.name = 'zhangsan'
    this.age = 23
  }
  Person.prototype.count = 50 
  function Son () {
    Person.apply(this)
    this.num = 18
  }

  var son = new Son()

  console.log(son.name) // zhangsan
  console.log(son.age)  // 23
  console.log(son.count)// undefined

构造函数继承就是在子级构造函数里面执行父级的构造函数并且改变this的指向

构造函数继承的优点:

子级直接继承父级构造函数的属性和方法

构造函数继承的缺点:

子类无法继承父类原型链上的属性和方法

3,混合继承

// 混合继承
  function Person () {
    this.name = 'zhangsan'
    this.age = 23
  }
  Person.prototype.count = 50 
  function Son () {
    Person.apply(this)
    this.num = 18
  }
  Son.prototype = new Person()
  var son = new Son()

  console.log(son.name) // zhangsan
  console.log(son.age)  // 23
  console.log(son.count)// 50

混合继承就是集合原型链继承和构造函数继承两者的优点集合在一起,集二者优点与一身

优点:

集合了原型链继承和构造函数继承的优点集合在一起

缺点:

子类会拥有两个原型链属性,不过可以忽略不计;

4,原型式继承

临时创建一个构造函数,利用临时构造函数的原型,在此基础上实例化对象;

// 原型是继承;
  function object(o){
    function F(){}
    F.prototype = o;
    return new F();
  } 

  var person = {
    name: 'zhangsan',
    friend: ['111', '222', '333']
  }
  var son = object(person)
  son.name = 'lisi'
  son.friend.push('444')

  console.log(son.name) // lisi
  console.log(son.friend) // '111', '222', '333', '444'

  var son2 = object(person)
  console.log(son2.name) // zhangsan
  console.log(son2.friend) // '111', '222', '333', '444'

缺点:

原型的引用类型会在各实例化对象中共享,一旦修改其中一个就会污染其他实例;

Object.create()方法

ES5通过Object.create()方法规范了原型式继承,可以接受两个参数,一个是用作新对象原型的对象和一个可选的为新对象定义额外属性的对象,行为相同,基本用法和上面的object一样,除了object不能接受第二个参数以外

var person = {
    name: 'Jiang',
    friends: ['Shelby', 'Court']
  }
  var anotherPerson = Object.create(person)
  console.log(anotherPerson.friends) // "Shelby", "Court"

5,寄生式继承;

寄生式继承的思路与寄生构造函数和工厂模式类似,即创建一个仅用于封装继承过程的函数

function createSuper (o) {
    var clone = Object.create(o) // 创建一个新对象
    clone.fn = function () {
      console.log('hello')
    }
    return clone
  }

  var person = {
    name: 'Jiang'
  }

  var anotherPeson = createSuper (person)
  anotherPeson.fn() // hello

基于person返回了一个新对象anotherPeson,新对象不仅拥有了person的属性和方法,还有自己的fn方法

6,寄生式组合函数;

使用寄生式组合模式相当于规避了混合继承里面重复父类实例的属性和方法在子类实例和原型里面重复出现;
基本思路是不必为了子类的原型而调用父类的构造函数,我们需要的只是父类原型的一个副本;
核心概念就是利用寄生式继承父类的原型,再把结果指定给子类的原型;

// 寄生式继承的方法;
  function createSuper(son, person) {
    var prototype = Object.create(Person.prototype);
    prototype.constructor = Son;
    Son.prototype = prototype;
  }
  function Person () {
    this.name = 'zhangsan'
    this.age = 23
  }
  Person.prototype.count = 50
  function Son () {
    // 继承属性
    Person.call(this, name)
    this.num = 18
  }
  createSuper(Son, Person)
  var son = new Son()

  console.log(son.name) // zhangsan
  console.log(son.age) // 23
  console.log(son.num) // 18
  console.log(son.count) // 50

  function Son2 () {

  }
  createSuper(Son2, Person)
  var son2 = new Son2()
  son2.count = 80

  console.log(son.count) // 50
  console.log(son2.count) // 80

在createSuper()函数中所做的事:

在createSuper函数中用到了ES5的object.create()方法,将超类型的原型指定为一个临时的空构造函数的原型,并返回构造函数的实例。
此时由于构造函数内部为空(不像Person里面有实例属性),所以返回的实例也不会自带实例属性,这很重要!因为后面用它作为Son 的原型时,就不会产生无用的原型属性了,借调构造函数也就不用进行所谓的“重写”了。
然后为这个对象重新指定constructor为Son ,并将其赋值给Son 的原型。这样,就达到了将超类型构造函数的实例作为子类型原型的目的,同时没有一些从Person继承过来的无用原型属性。

优点:

继承了混合继承的优点,避免了出现重复多个原型链属性

缺点:

感觉没什么缺点,如果非说有缺点,个人感觉就是不好理解

7,Es6的class继承

// Es6 class类的继承
  class Person {
    constructor (name, age) {
      this.name = name
      this.age = age
    }
    fn () {
      console.log(this.name)
    }
  }
  // let person = new Person('zhangsan', 23)
  // person.fn()
  class Son extends Person {
    constructor (name, age, count) {
      super(name, age)
      this.count = count
    }
    fn1 () {
      console.log(this.name, this.count)
    }
  }
  var son = new Son('小花', 18, 50)
  var son2 = new Son('小红', 20)
  son.fn() // 小花
  son.fn1() // 小花 50
  son2.fn() // 小红
  son2.fn1() // 小红 undefined

缺点:

就是兼容性

上一篇下一篇

猜你喜欢

热点阅读