js-day22-继承

75 阅读4分钟

继承

  • 继承是和构造函数相关的一个应用
  • 是指,让一个构造函数去继承另一个构造函数的属性和方法
  • 所以继承一定出现在 两个构造函数之间

原型继承

 // 父类构造函数
 function Father(name, age){
     this.name = name
     this.age = age
 }
 Father.prototype.money = function(){
     console.log('一个小目标!')
 }

 // 子类构造函数
 function Son(name, age){
     this.name = name
     this.age = age
 }

 // 怎么让子类构造函数去继承父类构造函数的方法呢?
 Son.prototype = Father.prototype

 // 例如:你给子类构造函数的原型上面添加一个方法
 Son.prototype.love = function(){
     console.log('喜欢谈👫🏻!')
 }

 let s = new Son('赵三', 20)
 s.money()
 s.love()
    

 let f = new Father('赵俊骏', 50)
 f.love()

 console.log(Father.prototype)
 console.log(Son.prototype)

注意点.jpg 关键代码: Son.prototype = Father.prototype

以上方法不太好,直接覆盖了原来的原型对象


 // 推荐使用的方式,把父类的实例化对象给子类的原型对象
 Son.prototype = new Father('赵三', 20)

 // 例如:你给子类构造函数的原型上面添加一个方法
 Son.prototype.love = function(){
     console.log(this.name + '喜欢谈👫🏻!')
 }

 let s = new Son()
 s.love()
 s.money()
 s.rg()

 console.log(Son.prototype)

 // let f = new Father('赵俊骏', 50)
        // f.love() // f.love is not a function

原型继承.jpg 把父类的实例化对象给子类的原型对象

原型继承,就是在本身的原型链上加一层结构

关键代码:Son.prototype = new Father('赵三', 20)

  • 原型继承优缺点:

优点:构造函数体内和原型上的都可以继承

缺点:

一个构造函数的内容,在两个位置传递参数

继承来的属性不再子类实例的身上

借用构造函数继承

把父类构造函数体借用过来使用一下而已

 // 父类构造函数
 // 第四步:在进入函数体内之前,先进行形参赋值 name='张三' age=18 
 function Father(name, age){
     // 第五步:改变了this执行,之前的this指向的是window,现在指向的是Son构造函数的实例化对象
     // 例如:new Son().name = name

     // 第五步:进行赋值操作
     this.name = name
     this.age = age

     // 注意点:构造函数也可以当成普通函数去调用,也可以实例化调用
     // console.log(this)
 }
 Father.prototype.money = function(){
     console.log('一个小目标')
 }

 // 子类构造函数
 // call() 改变函数内部的this指向,传递参数就是正常的传递(你给函数怎么传递参数的就怎么传参)

 // 第二步:在进入函数体内之前,先进行形参赋值 name='张三' age=18 sex='男'
 function Son(name, age, sex){
     // 第三步:执行Father.call(this, name, age)这个函数,call函数会立即调用,实际上去调用Father函数,在调用之前先传递实参 '张三', 18
     Father.call(this, name, age)
     this.sex = sex
 }
 // 第一步:当实例化对象的时候,调用函数并传递实参进去 '张三', 18, '男'
 const s = new Son('张三', 18, '男')
 console.log(s)

关键代码:Father.call(this, name, age)

  • 借用构造函数继承优缺点:

    优点:

    继承来的属性是在自己身上

    我们一个实例化过程在一个位置传递参数

    缺点:

    只能继承父类构造函数体内的内容

    父类原型上的内容不能继承

组合继承

  • 就是把 原型继承借用构造函数继承 两个方式组合在一起
// 父类构造函数
function Father(name, age){
    this.name = name
    this.age = age
}
Father.prototype.money = function(){
    console.log('一个小目标')
}

// 子类构造函数
function Son(name, age, sex){
    Father.call(this, name, age)
    this.sex = sex
}

Son.prototype = new Father()
Son.prototype.say = function(){
    console.log('喜欢说话!')
}

const s = new Son('张三', 18, '男')
console.log(s)
s.money()

s.say()

关键代码:

function Son() {
  Person.call(this)
}
Student.prototype = new Father()
  • 组合继承的优缺点:

    优点:

    父类构造函数体内和原型上的内容都能继承

    继承下来的属性放在自己身上

    在一个位置传递所有的参数

    缺点:

    当你给子类添加方法的时候,实际上是添加在父类的实例身上

ES6的继承

直接把继承方案变成了extends关键字了,直接使用即可

注意点: ES6的基础方案其实内部的实现依旧是组合继承

 class Father{
     constructor(name, age){
         this.name = name
         this.age = age
     }
     money(){
         console.log('一个小目标')
     }
 }

 class Son extends Father{
     constructor(name, age, sex){
         // 注意点:super() 超级函数必须先调用,再去定义子类的属性
         super(name, age)
         this.sex = sex
     }
 }

 const s = new Son('王成', 18, '男')
 console.log(s)
 s.money()

关键代码:

// 下面表示创造一个 Son 类,继承自 Father 类
class Son extends Father {
    constructor () {
        // 必须在 constructor 里面执行一下 super() 完成继承 
        super()
    }
}

拷贝继承

 const obj = {
   name: '张三',
   age: 18,
   say(){
       console.log('hello')
   }
}
const data = {}
for(let key in obj){
   data[key] = obj[key]
}
console.log(data)
data.say()

继承案例

  1. 第一个div拖拽
  2. 第二个div在拖拽的时候需要限定它拖拽的范围
 *{
   margin: 0;
   padding: 0;
}
html, body{
   height: 100%;
}
div{
   width: 200px;
   height: 200px;
   position: absolute;
}
.box1{
   background-color: hotpink;
}
.box2{
   background-color: orange;
}
<body>
  <div class="box1"></div>
  <div class="box2"></div>
  <script>
      // 获取元素
      // const box2 = document.querySelector('.box2')

      // 实现拖拽效果
      class Drag{
          constructor(ele){
              this.box = document.querySelector(ele)
              this.init()
          }
          init(){
              this.down()
              this.up()
          }
          down(){
              this.box.onmousedown = (e)=>{
                  let l = e.offsetX
                  let t = e.offsetY
                  this.move(l, t)
              }
          }
          move(l, t){
              document.onmousemove = (e)=>{
                  let x = e.clientX - l
                  let y = e.clientY - t
                  this.box.style.left = x + 'px'
                  this.box.style.top = y + 'px'
              }
          }
          up(){
              document.onmouseup = ()=>{
                  document.onmousemove = null
              }
          }
      }
      new Drag('.box1')
      
      // 需求:第二个div在拖拽的时候需要限定它拖拽的范围
      // 注意点:有继承后可以定制化开发一些东西,并且可以减少代码量,优化性能
      class Drag2 extends Drag{
          constructor(ele){
              super(ele)
          }
          // 重新move()方法
          move(l, t){
              document.onmousemove = (e)=>{
                  let x = e.clientX - l
                  let y = e.clientY - t

                  // 范围限定
                  if(x<=0){
                      x = 0
                  }else if(x>=window.innerWidth - this.box.offsetWidth){
                      x = window.innerWidth - this.box.offsetWidth
                  }
                  if(y<=0){
                      y = 0
                  }else if(y>=window.innerHeight - this.box.offsetHeight){
                      y = window.innerHeight - this.box.offsetHeight
                  }

                  this.box.style.left = x + 'px'
                  this.box.style.top = y + 'px'
              }
          }
      }
      new Drag2('.box2')

  </script>
</body>