37 JS继承

原型对象

360截图20220504192209030

new一个对象的过程

360截图20220504182639946

理解补充链接

https://juejin.cn/post/7075354546096046087#heading-1

知乎的

视频蛋老师

原型链继承

这种继承是有问题的:

  1. 创建child实例时不能传参,也就是Child构造函数本身不接受参数。

  2. 当原型上的属性是引用数据类型时,所有实例都会共享这个属性,即某个实例对这个属性重写会影响其他实例

    原因:子类实例的原型对象都指向new Parent()这个父类实例(的原型对象上),因此我们找不到子类实例自身没有name这个属性时,会自动去自己的原型对象(Child.prototype)上找,然后大家指向的都是父类的name这种引用类型,找的都是同一份数据

//原型链继承
  function Parent() {
    this.name = ['sikara']
    this.s = 1
    this.getS = function () {
      console.log(this.s)
    }
  }

  Parent.prototype.getName = function () {
     console.log(this.name)
  }

  function Child() {

  }
  //子类的原型链指向父类实例
// 等同于Child.prototype.__proto__ = Parent.prototype
  Child.prototype = new Parent()
	
  let child = new Child()
  console.log(child.name) //sikara
  console.log(child.s)  //1
  child.getName()

  //弊端  子类某一实例对父类引用类型变量的修改会影响所有子类实例
  let child1 = new Child()
  child1.name[0] = 'asfa'
  child1.s = 444
  console.log(child1.name)  //asfa
  console.log(child1.s)     //444
  console.log(child.name)   //asfa
  console.log(child.s)      //1

构造函数继承

  //解决原型链继承弊端------使用构造函数继承
  //定义:在子类的构造函数中执行父类的构造函数并且为其绑定子类的this
  function Animal(name) {
    this.name = [name]
  }

  Animal.prototype.getName = function () {
    console.log(this.name)
  }

  function Dog() {
      //创建一个对象,将父类构造函数内的属性赋值后转到该对象实例自身
    Animal.call(this,'sikara')
      //对于引用类型,对象是复制一份属性再添加到自身(深拷贝)
  }

  let dog = new Dog()
  let dog2 = new Dog()
  dog2.name[0] = 'aaa'
  console.log(dog.name) //sikara
  console.log(dog2.name)  //aaa
  //弊端  继承不到父类原型上的方法和属性
  dog2.getName()

组合式继承

  //解决原型链继承弊端------使用构造函数继承
  //定义:在子类的构造函数中执行父类的构造函数并且为其绑定子类的this
  function Animal(name) {
    this.name = [name]
  }

  Animal.prototype.getName = function () {
    console.log(this.name)
  }

  function Dog() {
    Animal.call(this,'sikara')
  }

// 等同于Dog.prototype.__proto__ = Animal.prototype
//这样Dog就能通过__proto__访问到父类原型对象上的方法了
  Dog.prototype = new Animal()
//使用原型链继承解决构造函数继承弊端:两者一同使用就是组合式继承 弊端就是每次实例化对象都需要new Animal()即多执行一次父类构造方法

  //原型链的一个规则
  Dog.prototype.constructor = Dog

  let dog = new Dog()
  let dog2 = new Dog()
  dog2.name[0] = 'aaa'
  console.log(dog.name) //sikara
  console.log(dog2.name)  //aaa
  
  dog2.getName()
  console.log(Animal.prototype.constructor)
  console.log(Dog.prototype.constructor)

寄生式继承

   const object = function (o) {
      function F() { this.s = 3}
      F.prototype = o
      return new F()
  }
 function FF() {
        this.n = 2
  }
  FF.prototype.add=9999 //无效
 FF.prototype= object(Dog.prototype)
  // FF.prototype= object(dog)//会多加一层dog的实例
  FF.prototype= Object.create(Dog.prototype)//弊端就是一旦对FF原型对象做出修改,自身原型对象属性将丢失(构造函数内的不受影响),建议是先继承,再为子类添加方法
  let f1 = new FF()

  console.log(f1) 

寄生组合式继承------避免了组合式继承两次调用父类构造方法

  function Animal(name) {
    this.name = [name]
  }

  Animal.prototype.getName = function () {
    console.log(this.name)
  }

  function Dog() {
    Animal.call(this,'sikara')
  }

// 简化了原型链继承调用构造函数写法(F是临时构造函数)
//等同于Dog.prototype .__proto__ = F.prototype = Animal.prototype
  Dog.prototype = Object.create(Animal.prototype)//{...Animal.prototype}不行  create不是简单的浅拷贝 

  //原型链的一个规则    原型对象.constructor = 原型对象对应的对象的构造方法
  Dog.prototype.constructor = Dog//原型链连接父类后(也即是对Dog.prototype做出了修改),子类自身的原型对象会缺少constructor属性,但不影响继承

  let dog = new Dog()
  let dog2 = new Dog()
  dog2.name[0] = 'aaa'
  console.log(dog.name) //sikara
  console.log(dog2.name)  //aaa
  dog2.getName()
//测试
console.dir(dog2)
  console.dir(dog2.__proto__)
  console.dir(dog2.__proto__.__proto__)
  console.dir(dog2.__proto__.__proto__.__proto__)
  console.dir(dog2.__proto__.__proto__.__proto__.__proto__)