JS继承的几大方法

原文链接: https://juejin.im/post/6844904098941108232

这篇是阅读文章,自己在控制台打印,记录一下,方便后期复习

原文写得很好,容易理解,对继承不太清楚的可以看看

 

// 一,原型链继承

  // function Parent () {
  //   this.name = 'Parent'
  //   this.sex = 'boy'
  // }
  // Parent.prototype.getName = function () {
  //   console.log(this.name)
  // }
  // function Child () {
  //   this.name = 'child'
  // }
  // 将子类的原型对象指向父类的实例或者指向父类的原型对象
  // Child.prototype = new Parent()
  // Child.prototype = Parent.prototype
  
  // var child1 = new Child()
  // child1.getName()
  // console.log(child1)

  // 将上面的原型方法再添加一个
  // Parent.prototype.getSex = function () {
  //   console.log(this.sex)
  // }

  // 使用这种方法只能继承到父类原型链上的方法,构造函数上的属性和方法将不会被继承
  // Child.prototype = Parent.prototype
  // 使用父实例继承,可以继承构造函数和原型对象上的方法
  // Child.prototype = new Parent()

  // var child1 = new Child()
  // child1.getName()
  // child1.getSex() // undefined
  // console.log(child1)

  /** --------------------------------------- */

  // function Parent (name) {
  //   this.name = name
  //   this.sex = 'boy'
  //   this.colors = ['white', 'black']
  // }
  // function Child () {
  //   this.feature = ['cute']
  //   this.colors = ['white', 'red']
  // }
  // var parent = new Parent('parent')
  // Child.prototype = parent
  
  // Child.prototype.age = 17
  // var child1 = new Child('child1')
  // child1.sex = 'girl'
  // child1.colors.push('yellow')
  // child1.feature.push('sunshine')
  
  // var child2 = new Child('child2')
  
  // console.log(child1) // colors: (3) ["white", "red", "yellow"] feature: (2) ["cute", "sunshine"] sex: "girl"
  // console.log(child2) // colors: (2) ["white", "red"]  feature: ["cute"]
  
  // console.log(child1.name) // parent
  // console.log(child2.age) // 17
  
  // console.log(parent) // colors: (2) ["white", "black"]  name: "parent"  sex: "boy" age:17

  // 原型链继承缺点
  // 1.子类原型新增方法和属性要放在继承之后
  // 2.无法实现多继承
  // 3.来自原型对象的所有属性都被共享了,如果修改原型对象的属性,所有创建的实例对象都会受到影响
  // 4.创建子类时,不能向父类构造函数传参

   /** --------------------------------------- */

   // instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
   // a instanceof B

  // 实例对象a instanceof 构造函数B

  // 检测a的原型链(__proto__)上是否有B.prototype,有则返回true,否则返回false。
  // function Parent () {
  //   this.name = 'parent'
  // }
  // function Child () {
  //   this.sex = 'boy'
  // }
  // Child.prototype = new Parent()
  // var child1 = new Child()
  
  // console.log(child1 instanceof Child) // true
  // console.log(child1 instanceof Parent) // true
  // console.log(child1 instanceof Object) // true

  /** --------------------------------------- */

  // isPrototypeOf()的用法和instanceof相反。
  // 它是用来判断指定对象object1是否存在于另一个对象object2的原型链中,是则返回true,否则返回false。

  // function Parent () {
  //   this.name = 'parent'
  // }
  // function Child () {
  //   this.sex = 'boy'
  // }
  // Child.prototype = new Parent()
  // var child1 = new Child()
  
  // console.log(Child.prototype.isPrototypeOf(child1)) // true
  // console.log(Parent.prototype.isPrototypeOf(child1)) // true
  // console.log(Object.prototype.isPrototypeOf(child1)) // true

  /** --------------------------------------- */
  
  // 3.构造函数继承
  // call接受若干个参数 apply接受一组数组 bind不会立即执行,返回函数
  // function Parent (name) {
  //   this.name = name
  // }
  // function GrandParent (age) {
  //   this.age = age
  // }
  // function OtherParent (color) {
  //   this.color = color
  // }

  // function Child () {
  //   this.sex = 'boy'
  //   this.name = 'juan'
  //   Parent.call(this, 'child')
  //   this.name = 'junn'
  //   GrandParent.apply(this, [17, 'red'])
  // }

  // var child1 = new Child()
  // OtherParent.bind(child1, 'yellow')()
  // console.log(child1) // {sex: "boy", name: "junn", age: 17, color: "red"}

  /** --------------------------------------- */

  // function Parent (name, sex) {
  //   this.name = name
  //   this.sex = sex
  //   this.colors = ['white', 'black']
  // }
  // function Child (name, sex) {
  //   Parent.call(this, name, sex)
  // }
  // var child1 = new Child('child1', 'boy')
  // child1.colors.push('yellow')
  
  // var child2 = new Child('child2', 'girl')
  // console.log(child1) // {name: "child1", sex: "boy", colors: Array(3)}
  // console.log(child2) // {name: "child2", sex: "girl", colors: Array(2)}

  // 构造继承的优点: 1.可以继承多个父类 2.修改实例不会影响其他实例  3.可以传递参数

  /** --------------------------------------- */

  // const arr = ['5', '6', '7', '11']
  // const res = Math.max.apply(null, arr)
  // console.log(res)

  // function Parent (name) {
  //   this.name = name
  // }
  // Parent.prototype.getName = function () {
  //   console.log(this.name)
  // }
  // function Child () {
  //   this.sex = 'boy'
  //   Parent.call(this, 'good boy')
  // }
  // Child.prototype.getSex = function () {
  //   console.log(this.sex)
  // }
  // var child1 = new Child()
  // console.log(child1)
  // child1.getSex()
  // child1.getName() // 报错

  // 构造函数的缺点: 不能继承父类构造函数原型对象上的方法和属性

  /** --------------------------------------- */

  // function Parent (name) {
  //   this.name = name
  // }
  // function Child () {
  //   this.sex = 'boy'
  //   Parent.call(this, 'child')
  // }
  // var child1 = new Child()
  
  // console.log(child1)
  // console.log(child1 instanceof Child) // true
  // console.log(child1 instanceof Parent) // false
  // console.log(child1 instanceof Object) // true

  // 通过构造函数继承的实例不是继承父类的实例,也不能使用父类原型对象上的方法

  /** --------------------------------------- */

  // 4.组合继承

  // 组合继承就是将原型链继承与构造函数继承组合在一起,从而发挥两者之长的一种继承模式

  // function Parent (name) {
  //   this.name = name
  // }
  // Parent.prototype.getName = function () {
  //   console.log(this.name)
  // }
  // function Child (name) {
  //   this.sex = 'boy'
  //   Parent.call(this, name)
  // }
  // Child.prototype = new Parent()
  // Child.prototype.getSex = function () {
  //   console.log(this.sex)
  // }
  // var parent1 = new Parent('parent1')
  // var child1 = new Child('child1')
  // console.log(child1) // Child{ name: 'child1', sex: 'boy' }
  // console.log(parent1)// Parent{ name: 'parent1' }
  // child1.getName()    // 'child1'
  // child1.getSex()     // 'boy'
  // parent1.getName()   // 'parent1'
  // parent1.getSex()    // Uncaught TypeError: parent1.getSex is not a function

  /** --------------------------------------- */

  // function Parent (name) {
  //   this.name = name
  // }
  // Parent.prototype.getName = function () {
  //   console.log(this.name)
  // }
  // function Child (name) {
  //   this.sex = 'boy'
  //   Parent.call(this, name)
  // }
  // Child.prototype = new Parent()
  // Child.prototype.getSex = function () {
  //   console.log(this.sex)
  // }
  
  // var child1 = new Child('child1')
  // var parent1 = new Parent('parent1')
  // console.log(child1.constructor)  // parent
  // console.log(parent1.constructor) // parent

  // 原型链继承会切断child原来的原型链结构,需要加上Child.prototype.constructor = Child修正

  // constructor它是构造函数原型对象中的一个属性,正常情况下它指向的是原型对象。
  // 它并不会影响任何JS内部属性,只是用来标示一下某个实例是由哪个构造函数产生的而已。
  // 如果我们使用了原型链继承或者组合继承无意间修改了constructor的指向,那么出于编程习惯,我们最好将它修改为正确的构造函数。

  /** --------------------------------------- */

  // function Parent (name, colors) {
  //   this.name = name
  //   this.colors = colors
  // }
  // Parent.prototype.features = ['cute']
  // function Child (name, colors) {
  //   this.sex = 'boy'
  //   Parent.apply(this, [name, colors])
  // }
  // Child.prototype = new Parent()
  // // Child.prototype.constructor = Child
  // Child.prototype.age = '17'
  
  // var child1 = new Child('child1', ['white'])
  // child1.colors.push('yellow')
  // child1.features.push('sunshine')
  // var child2 = new Child('child2', ['black'])
  // var par = new Parent('child2', ['black'])

  // console.log(child1)
  // console.log(child2)
  // console.log(Child.prototype)
  // console.log(child1.features)
  // console.log(child2.features)
  // console.log(par.features)
  
  // console.log(child1 instanceof Child)
  // console.log(Child.prototype.isPrototypeOf(child1))
  // console.log(child1 instanceof Parent)

   /** --------------------------------------- */

  // function Parent (name) {
  //   console.log(name) // 这里有个console.log()
  //   this.name = name
  // }
  // function Child (name) {
  //   Parent.call(this, name)
  // }
  // Child.prototype = new Parent()
  // var child1 = new Child('child1')
  
  // console.log(child1)
  // console.log(Child.prototype)

  // 组合继承的缺点: 1 使用组合继承时,父类构造函数会被调用两次
  // 2 并且生成了两个实例,子类实例中的属性和方法会覆盖子类原型(父类实例)上的属性和方法,所以增加了不必要的内存。

  /** --------------------------------------- */

  // 组合继承
  // 实现方式:

  // 使用原型链继承来保证子类能继承到父类原型中的属性和方法
  // 使用构造继承来保证子类能继承到父类的实例属性和方法
  // 优点:

  // 可以继承父类实例属性和方法,也能够继承父类原型属性和方法
  // 弥补了原型链继承中引用属性共享的问题
  // 可传参,可复用
  // 缺点:

  // 使用组合继承时,父类构造函数会被调用两次
  // 并且生成了两个实例,子类实例中的属性和方法会覆盖子类原型(父类实例)上的属性和方法,所以增加了不必要的内存。
  // constructor总结:

  // constructor它是构造函数原型对象中的一个属性,正常情况下它指向的是原型对象。
  // 它并不会影响任何JS内部属性,只是用来标示一下某个实例是由哪个构造函数产生的而已。
  // 如果我们使用了原型链继承或者组合继承无意间修改了constructor的指向,那么出于编程习惯,我们最好将它修改为正确的构造函数。

  /** --------------------------------------- */

  // 5.寄生组合继承

  // function Parent (name) {
  //   this.name = name
  // }
  // Parent.prototype.getName = function () {
  //   console.log(this.name)
  // }
  // function Child (name) {
  //   this.sex = 'boy'
  //   Parent.call(this, name)
  // }
  // // 与组合继承的区别
  // Child.prototype = Object.create(Parent.prototype)
  // Child.prototype.constructor = Child
  
  // var child1 = new Child('child1')
  
  // console.log(child1) // Child{ sex: "boy", name: "child1" }
  // child1.getName() // "child1"
  
  // console.log(child1.__proto__) // Parent{}
  // console.log(Object.create(null)) // {}
  // console.log(new Object()) // {}

  //   寄生组合继承算是ES6之前一种比较完美的继承方式吧。

  // 它避免了组合继承中调用两次父类构造函数,初始化两次实例属性的缺点。

  // 所以它拥有了上述所有继承方式的优点:

  // 只调用了一次父类构造函数,只创建了一份父类属性
  // 子类可以用到父类原型链上的属性和方法
  // 能够正常的使用instanceOf和isPrototypeOf方法

  /** --------------------------------------- */

  // 6.原型继承

  // function Create(obj) {
  //   function F() {}
  //   F.prototype = obj
  //   return new F()
  // }

  // var cat = {
  //   heart: '❤️',
  //   colors: ['white', 'black']
  // }
  
  // var guaiguai = Create(cat)
  // var huaihuai = Create(cat)
  
  // console.log(guaiguai)
  // console.log(huaihuai)
  
  // console.log(guaiguai.heart)
  // console.log(huaihuai.colors)

  // 该方法的原理是创建一个构造函数,构造函数的原型指向对象,然后调用 new 操作符创建实例,并返回这个实例,本质是一个浅拷贝。

  // 在ES5之后可以直接使用Object.create()方法来实现

  /** --------------------------------------- */

  // 7.寄生式继承

  // var cat = {
  //   heart: '❤️',
  //   colors: ['white', 'black']
  // }
  // function createAnother (original) {
  //     var clone = Object.create(original);
  //     clone.actingCute = function () {
  //       console.log('我是一只会卖萌的猫咪')
  //     }
  //     return clone;
  // }
  // var guaiguai = createAnother(cat)
  // var huaihuai = Object.create(cat)
  
  // guaiguai.actingCute()
  // console.log(guaiguai.heart)
  // console.log(huaihuai.colors)
  // console.log(guaiguai)
  // console.log(huaihuai)

  // 实现方式:

  // 在原型式继承的基础上再封装一层,来增强对象,之后将这个对象返回。
  // 优点:

  // 再不用创建构造函数的情况下,实现了原型链继承,代码量减少一部分。
  // 缺点:

  // 一些引用数据操作的时候会出问题,两个实例会公用继承实例的引用数据类
  // 谨慎定义方法,以免定义方法也继承对象原型的方法重名
  // 无法直接给父级构造函数使用参数

  /** --------------------------------------- */

  // 8. 混入方式继承多个对象

  // function Parent() {
  //   this.name = 'parent'
  // }
  // Parent.prototype.getName = function() {
  //   console.log('parent')
  // }
  // function OtherParent(sex) {
  //   this.sex = sex
  // }
  // OtherParent.prototype.getColor = 'red'

  // function Child (sex) {
  //   Parent.call(this)
  //   OtherParent.call(this, sex)
  // }
  // Child.prototype = Object.create(Parent.prototype)
  // Object.assign(Child.prototype, OtherParent.prototype)
  // Child.prototype.constructor = Child

  // var child1 = new Child('boy')
  // // console.log(child1)
  // // child1.getName()
  // // console.log(child1.getColor)

  // console.log(Child.prototype.__proto__ === Parent.prototype) // true
  // console.log(Child.prototype.__proto__ === OtherParent.prototype) // false
  // console.log(child1 instanceof Parent) // true
  // console.log(child1 instanceof OtherParent) // false

  /** --------------------------------------- */

  // 9. class中的继承

  // class中的继承主要是extends和super

  // class Parent {
  //   constructor (name) {
  //     this.name = name
  //   }
  //   getName() {
  //     console.log(this.name)
  //   }
  // }

  // class Child extends Parent {
  //   // constructor (name, sex) {
  //   //   super(name)
  //   //   this.sex = sex
  //   // }
  //   sex = 'boy' // 等同于注释部分
  // }

  // var child1 = new Child('child1', 'boy')
  // console.log(child1)
  // child1.getName()

  // console.log(child1 instanceof Child)
  // console.log(child1 instanceof Parent)

  // extends 和 super必须一起使用
  // 子类必须得在constructor中调用super方法,否则新建实例就会报错,
  // 因为子类自己没有自己的this对象,而是继承父类的this对象,然后对其加工,如果不调用super的话子类就得不到this对象。

  /** --------------------------------------- */

  // class Parent {
  //   constructor () {
  //     console.log(new.target.name) // 此方法将调用两次
  //   }
  // }

  // class Child extends Parent {
  //   constructor () {
  //     const instance = super()
  //     console.log(instance)
  //     console.log(instance === this)
  //   }
  // }

  // const child1 = new Child()
  // const parent1 = new Parent()
  // console.log(child1, parent1)

  // Child
  // Child {}
  // true
  // Parent
  // Child{} Parent{}

  /** --------------------------------------- */

  // class Parent {
  //   constructor (name) {
  //     this.name = name
  //   }
  // }
  // class Child extends Parent {
  //   constructor (name) {
  //     this.sex = 'boy' // this调用必须放到super之后
  //     super(name)
  //   }
  // }
  // var child1 = new Child('child1')
  // console.log(child1)

  // 子类constructor中如果要使用this的话就必须放到super()之后
  // super当成函数调用时只能在子类的construtor中使用

  /** --------------------------------------- */

  // class Parent {
  //   constructor (name) {
  //     this.name = name
  //   }
  //   getName() {
  //     console.log(this.name)
  //   }
  // }
  // Parent.prototype.getSex = () => {
  //   console.log('boy')
  // }
  // Parent.getColors = () => {
  //   console.log(['red'])
  // }

  // class Child extends Parent {
  //   constructor (name) {
  //     super(name)
  //     super.getName()
  //   }
  //   initGetSex () {
  //     super.getSex()
  //   }
  //   static initColor () {
  //     super.getColors()
  //   }
  // }

  // const child1 = new Child('child1')
  // child1.initGetSex()
  // Child.initColor()
  // console.log(child1)

  /** --------------------------------------- */

  // class Parent {
  //   constructor () {}
  // }
  // Parent.prototype.sex  = 'boy'
  // Parent.prototype.getSex = function () {
  //   console.log(this.sex)
  // }
  // class Child extends Parent {
  //   constructor () {
  //     super()
  //     this.sex = 'girl'
  //     super.getSex()
  //   }
  // }
  // var child1 = new Child()
  // console.log(child1)

  // ES6规定,通过super调用父类的方法时,super会绑定子类的this

  /** --------------------------------------- */

  // function Parent () {
  //   this.name = 'parent'
  // }
  
  // class Child1 extends Parent {}
  // class Child2 {}
  // class Child3 extends Array {}
  // var child1 = new Child1()
  // var child2 = new Child2()
  // var child3 = new Child3()
  // child3[0] = 1
  
  // console.log(child1)
  // console.log(child2)
  // console.log(child3)

  // extends后面接着的继承目标不一定要是个class。

  // class B extends A {},只要A是一个有prototype属性的函数,就能被B继承。

  // 由于函数都有prototype属性,因此A可以是任意函数。

  /** --------------------------------------- */

  // 总结:
  /**
   * ES6中的继承:

    主要是依赖extends关键字来实现继承,且继承的效果类似于寄生组合继承
    使用了extends实现继承不一定要constructor和super,因为没有的话会默认产生并调用它们
    extends后面接着的目标不一定是class,只要是个有prototype属性的函数就可以了
    super相关:

    在实现继承时,如果子类中有constructor函数,必须得在constructor中调用一下super函数,因为它就是用来产生实例this的。
    super有两种调用方式:当成函数调用和当成对象来调用。
    super当成函数调用时,代表父类的构造函数,且返回的是子类的实例,也就是此时super内部的this指向子类。在子类的constructor中super()就相当于是Parent.constructor.call(this)。
    super当成对象调用时,普通函数中super对象指向父类的原型对象,静态函数中指向父类。且通过super调用父类的方法时,super会绑定子类的this,就相当于是Parent.prototype.fn.call(this)。
    ES5继承和ES6继承的区别:

    在ES5中的继承(例如构造继承、寄生组合继承) ,实质上是先创造子类的实例对象this,然后再将父类的属性和方法添加到this上(使用的是Parent.call(this))。
    而在ES6中却不是这样的,它实质是先创造父类的实例对象this(也就是使用super()),然后再用子类的构造函数去修改this。
   */

 

posted @ 2020-10-19 15:56  潇湘羽西  阅读(199)  评论(0编辑  收藏  举报