7、class的继承—ES6学习笔记

extends、 static、 super

ES6中extends关键字,可以很方便的实现子类继承父类,同时static关键字可以为类指定静态方法,以及super关键字可以在子类继承父类的时候,方便的去调用父类原型身上的方法或者静态方法。

本节我们将通过具体的例子来阐述着三个关键字如何使用?

首先我们在html中写了一个简单的canvas画布以及一些简单的样式阴影,这样我们可以很方便的看到这个画布在文档中的位置。
index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title></title>
  <style>
    canvas {
      box-shadow: 2px 2px 12px rgba(0, 0, 0, 0.5);
    }
  </style>
</head>
<body>
  <canvas id="canvas"></canvas>
  <script src="./js/bundle.js"></script>
</body>
</html>

如图所示:

需求:
我们要写一个小球的类,通过实例化这个小球,将小球绘制在画布当中。
并且让小球做一个自由落体的运动。

// 1、获取这个画布
const canvas = document.querySelector('#canvas');
// 2、获取一下这个画布对应的绘图环境
const ctx = canvas.getContext('2d');

// 3、初始化两个常量,来指定这个画布的分辨率
const w = canvas.width = 600;
const h = canvas.height = 400;

// 4、现在我们通过class关键字声明一个Ball小球的类
class Ball {
  /* 5、首先指定一下它的构造函数constructor,
  同时化初始化三个参数x,y,r,分别对应小球的横坐标、纵坐标、以及小球的半径 */
  constructor(x, y, r){
    this.x = x;
    this.y = y;
    this.r = r;
    /* 6、接下来再给小球添加一个color属性,让它随机的等于一个rgb值
          这里需要用到一个产生随机数的方法,但是这个方法和绘制小球实际上并没有太多的关系,
          所有我们把这个方法写成Ball类的静态方法,转至第7步
      10、 接下来我们做一下字符串的拼接,这里我就使用Ball类下面的rpFn()函数获取随机数
           使用两个~~号去掉小数部分,
    */
    this.color = `rgb(${~~Ball.rpFn([55, 255])}, ${~~Ball.rpFn([55, 255])}, ${~~Ball.rpFn([55, 255])})`;
    // 11、最后我们return this,这样方便在实例化的时候调到其它的方法
    return this;
  }
  /* 12、 为了让小球绘制到画布当中,我们在写一个render方法
          render方法接收一个参数,就是绘图的环境ctx
  */
  render(ctx){
    // 13、首先保存一下绘图环境
    ctx.save();
    // 14、绘制小球的坐标
    ctx.translate(this.x, this.y);
    // 15、指定一下小球的填充颜色
    ctx.fillStyle = this.color;
    // 16、起始一个新的路径
    ctx.beginPath();
    // 17、绘制小球的路径
    ctx.arc(0, 0, this.r, 0, 2*Math.PI);
    // 18、给小球填充颜色
    ctx.fill();
    // 19、恢复一下绘图环境
    ctx.restore();
    // 20、返回teturn this
    return this;
  }
  /* 7、如何指定静态方法 前面我们已经提了,使用static关键字
     这里只需要写上static,然后跟上方法的名字就可以了,这里叫rpFn
     给它接收一个参数数组
     使用它的时候,如:Ball.rpFn([1, 10]),那么它就可以返回一个1-10之间的随机数
  */
  static rpFn(arr){ // Ball.rpFn([1, 10])
    // 8、 接下来我们就来实现这个函数,首先声明两个变量
    // 获取到这个数组的最大值和最小值,使用...作为扩展运算符
    let max = Math.max(...arr),
        min = Math.min(...arr);
    // 9、返回一组固定区间的随机数 ,这样我们获取随机数的函数就写好了
    return Math.random() * (max - min) + min;
  }
}

/*
  21、声明一个变量,实例测试一下
  在坐标100,100的位置,绘制一个半径为30的小球
  此时我们发现一个小球就绘制在画布当中了
*/
// const ball1 = new Ball(100, 100, 30).render(ctx);

/*
  22、接下来我任然绘制一个小球在画布当中,并且让它做一个自由落体的运动。
      那么我们可以肯定接下来绘制的这个小球依然有Ball所有的属性以及绘制
      的render方法。

     我们声明一个新的类,起名叫SuperBall,它可以先去继承Ball的类,
     把它的属性和方法继承过来
*/
//    子类      继承    父类
class SuperBall extends Ball {
  // 23、此处如果什么都不写,相当于复制了一个Ball类
  // 24、接下来我们就给SuperBall指定一些它自己的属性和方法
  // 首先写上它的构造函数constructor,这里面依然需要传入x,y,r三个参数
  constructor(x, y, r){
    /*
    25、我们首先要继承x,y,r三个属性,
    我们去调用一下父类的构造函数,发现报错了

    */
    // Ball.call(this, x, y, r); 
    // 报错 找不到this,实际上通过class定义的类在ES6中它是不允许通过函数的方式去调用的,
    // 也就是说这种写法本身就是错的
    // this.color = 'red';

    /*
      26、那么如何在子类中调用父类的构造函数呢?
      在ES6当中提供了一个新的关键字,super
      super主要有两种用法
      第一种:就是在子类继承父类的时候,
      在子类的构造函数中可以当成一个函数去使用,
      当它当作函数去使用的时候,就相当于调用了父类的构造函数
      这里由于要继承x,y,r三个属性,所有要把这三个参数传进去
    */
    super(x, y, r); //这个时候我们就发现一个小球就绘制在相同的位置了
    /*
    27、super(x, y, r);调用完成之后,实际上就相当于子类继承了父类构造函数当中的所有的属性,
    在ES6中规定在没调用super()函数之前子类是没有自己的this的

    */
    //纵向的速度vy
    //28、也就是说当子类继承父类的时候不仅仅是继承属性,还会自动继承静态方法
    this.vy = SuperBall.rpFn([2, 4]);
    this.g = SuperBall.rpFn([0.2, 0.4]);
    this.a = 0;
    return this;
  }
  // 28、为了让小球动起来,我们再写一个move方法
  move(ctx){
    // super()在非构造函数中调用时会报错,super()只能在构造函数中调用
    // super();  // 报错

    this.y += this.vy;
    this.vy += this.g;

    let current = this.vy * -0.75;

    if(this.y + this.r >= ctx.canvas.height){
      this.y = ctx.canvas.height - this.r;

      if(Math.abs(current - this.a) < 0.01) return false;

      this.a = this.vy *= -0.75; 
    }
    // 29、清除整个画布大小
    ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
    // 30、将绘图打印出来,这里render不是当作函数调用
    // super的第二种使用情况:
    // 如果在原型的方法里使用super,它实际上指向的是父类的原型对象
    super.render(ctx);

    return true;
  }
};

// 我们发现一个小球就绘制在了相同的位置了
// const ball1 = new SuperBall(100, 100, 30).render(ctx);

/*
我们想鼠标点击哪里,哪里就出现一个小球,并且呈现自由落体运动
*/
let ball, timer;

canvas.onclick = function (e){
  // 31、获取点击画布的鼠标坐标
  let x = e.offsetX, y = e.offsetY;
  // 通过Ball的静态方法来指定一个随机的半径
  let r = ~~Ball.rpFn([25, 55]);

  //33、为了每次点击值产生一个小球,故每次点击之前最画布进行一次清除
  ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
  // 32、接受一个实例对象
  ball = new SuperBall(x, y, r).render(ctx);
  // 33、调用一下小球掉下来的方法
  ballMove();
}

function ballMove(){
  // 34、用timer变量接收一下动画帧的返回值
  timer = window.requestAnimationFrame(ballMove);
  //35、如果ball.move(ctx)返回了一个false
  //就清除这个动画帧
  if(!ball.move(ctx)){
    window.cancelAnimationFrame(timer);
  }
}

我们做一下简单的总结回顾:
首先在Ball这个类中,我们通过static关键字来指定了一个镜静态方法,静态方法实际上就是挂载到类身上的方法,
接下来我们实现了用extends,用子类去继承父类,并且想给子类去指定一些额外的属性,首先需要通过super关键字调用一下父类的构造函数,这里面想继承那些属性就要把哪些属性当参数传到方法上,
接下来在子类的原型方法当中,如果是super()当作一个函数去调用的时候,就会报错,
此外,在构造函数当中在没使用super()之前是拿不到this 对象的,必须先调用super()才能调用子类的this对象。
接下来在原型的方法当中还有第二种情况,super可以当作一个对象去调用,它实际上指向的是父类原型的对象,并且在调用父类原型身上的方法的时候,会自动的绑定子类的this

子类继承父类 使用extends 关键字
为父类指定静态方法,使用 static 方法名字
super
- 在构造函数中可以当做一个函数来使用,相当于调用父类的构造函数
- 在原型方法中,可以当一个对象类使用,相当于父类的原型对象,并且会自动绑定this到子类上

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值