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

来源:互联网 发布:欧洲史 知乎 bbc 编辑:程序博客网 时间:2024/06/05 20:33

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到子类上

原创粉丝点击