es6 class及继承

构造函数

es5 和 es6构造函数

JS语言的传统方法是通过构造函数定义并生成新对象,是一种基于原型的面相对象的系统。

// 构造函数
const P = function(a, b) {
  this.a = a
  this.b = b
  return this // 返回this 可以链式调用
}
// 原型
P.prototype = {
  constructor: P,
  print: function() {
    console.log(this.a, this.b)
  }
}
const p = new P('hello', 'world').print() // hello world

构造函数中new的作用可以参考文章js构造函数和new
这里我们输出一下实例new P('hello', 'world')
在这里插入图片描述
如果我们在P.prototype中不指定constructor,此时创建的实例中也不会有这个,因为P.prototype = {xxx}其实是重写了它的原型,所以必须手动指定constructor
这里说明一句es5 中,每一个对象都有__proto__属性,指向对应的构造函数的prototype
es6 中新增加了类的概念,可以使用关键字 class声明一个类,之后以这个类来实例化对象。

class P {
  // 构造函数
  constructor(a, b) {
    this.a = a
    this.b = b
    return this
  }
  // 类方法
  print() {
    console.log(this.a, this.b)
  }
}
const p = new P('hello', 'world').print() // hello world

当我们每次执行 new 的时候都会执行 constructor 方法(所以参数我们都是从这里传入,在constructor中赋值给新的对象的,constructor是用来实例化时进行初始化的),this 指向的就是这个新实例化的对象
此时我们再来看new P('hello', 'world')的输出结果
在这里插入图片描述
我们发现,不需要再显示的指定constructor了。
总结:其实es6的class只是es5的构造函数的语法糖,但是这样写会使得我们的构造函数更清晰。

1、P中的constructor方法是构造函数,this关键字则代表实例对象,也就是说,es5 的构造函数P,对应es6的P的这个类的构造函数
2、构造函数的prototype属性,在es6 的类上继续存在,只是类的所有方法都定义在类的prototype属性上面
3、定义在类中的方法都是不可枚举的
4、constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法,一个类必须有constructor方法,如果没有显示定义,一个空的constructor方法会被默认添加
5、es5的构造函数如果不使用new运算符,那就是个普通的函数可以直接调用,但是es6的类不可以直接被调用,必须有new命令

静态方法

类相当于实例的原型,所有在类中定义的方法,都会被实例继承,如果一个方法前加上 static 关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就成为“静态方法”。

class Foo {
  static classMethod() {
    return 'hello';
  }
}

Foo.classMethod() // 'hello'

var foo = new Foo();
foo.classMethod() // TypeError: foo.classMethod is not a function

Foo 类的classMethod 方法是静态方法,可以直接在类上调用Foo.classMethod() ,而不是在实例上调用,因为实例上没有这个方法,同时,静态方法中的this指向的是这个类,而不是实例。

静态属性

类似于静态方法,静态属性指的是类(Class)本身的属性,即 Class.propName ,而不是在实例对象(this)上的属性

class Foo {
  static prop = 1;
}

class继承

Ball 类

这里我们用一个具体案例来讲解class的继承

<canvas id="canvas"></canvas>

创建一个画布,实现的效果是点击画布生成一个小球并做自由落体运动,直到停止

const canvas = document.querySelector('#canvas')
const ctx = canvas.getContext('2d')
const w = canvas.width = 600
const h = canvas.height = 400

/* 创建 “球” 类 */
class Ball{
  // 构造函数
  constructor(x, y, r) {
    this.x = x
    this.y = y
    this.r = r
    this.color = `rgb(${Ball.rpFn([55, 255])}, ${Ball.rpFn([55, 255])}, ${Ball.rpFn([55, 255])})`
    return this
  }
  // 原型方法
  render(ctx) {
    ctx.save()
    ctx.translate(this.x, this.y)
    ctx.fillStyle = this.color
    ctx.beginPath()
    ctx.arc(0, 0, this.r, 0, 2 * Math.PI)
    ctx.fill()
    ctx.restore()
    return this
  }
  // 静态方法生成颜色的随机数 Ball.rpFn调用
  static rpFn(arr) {
    let max = Math.max(...arr)
    let min = Math.min(...arr)
    return Math.floor(Math.random() * (max - min) + min)
  }
}

const ball = new Ball(100, 100, 30).render(ctx)
console.log(ball)

输出一个 Ball 类的实例看
在这里插入图片描述

类的继承

super

我们创建一个 SuperBall 类,继承 Ball 类,我们先什么内容都不写,此时我们输出看这个 SuperBall 类的实例

class SuperBall extends Ball{
  // constructor(x, y, r) {
  //  super(x, y, r)
  // }
}

const ball = new SuperBall(100, 100, 30).render(ctx)
console.log(ball)

在这里插入图片描述
回忆一下这个知识点:对象的__proto__属性,指向对应的构造函数的prototype
这里我们的 SuperBall 类,写没写 constructor 输出的结构都是一样的,那constructor 的作用是什么呢?
看下面的测试

class SuperBall extends Ball{
  // constructor(x, y, r) {
  //   super(x, y, r) // 相当于执行了父类的构造函数
  // }
  testThis() {
    console.log(this.r)
    return this
  }
}

const ball = new SuperBall(100, 100, 30).testThis() // 报错 undefined

es6 规定,在没有调用 super 之前子类是没有自己的this,所以调用就会报错 undefined。因为后续我们还需要对 SuperBall 类添加构造函数属性,需要用到 this ,所以这里需要使用 super 继承。
注意,super在构造函数中可以直接当做函数使用super(x, y, r),但是在原型中它指向的是父类的原型对象,所以需要通过super.xxx()方式调用方法
如果子类需要有自己的初始化过程,有自己的constructor函数,那么必须调用父类的构造函数也就是super(),不然没有this

(我们创建子类的实例的时候,如果我们的子类没有constructor那就会直接执行父类的constructor函数,因为上面我们说了,实例化new的时候其实就是调用了这个类的构造函数constructor,但是如果我们的子类有自己想要的属性,也就是子类也会有构造函数constructor的时候,此时就是我们将父类的构造函数重写了,所以我们需要调用super(x, y)并将参数传入来调用父类的构造函数,如果我们不调用super就不会执行父类的构造函数,那父类构造函数中的属性我们子类就拿不到了。)

继承 Ball 类的 SuperBall 类

下面我们给 SuperBall 类添加具体内容

class SuperBall extends Ball{
constructor(x, y, r) {
  super(x, y, r)
  this.vy = 3
  this.g = 0.3
  this.a = 0
  return this
}
move(ctx) {
  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
  }
  ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height)
  super.render(ctx) // 原型中调用super指向的是父类的原型对象
  return true
}
}

给画布添加点击事件,在点击的地方绘制小球

let ball, timer
canvas.onclick = function(e) {
  let x = e.offsetX, y = e.offsetY
  let r = ~~Ball.rpFn([25, 55])
  ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height)
  ball = new SuperBall(x, y, r).render(ctx)
  ballMove()
}
function ballMove() {
  timer = window.requestAnimationFrame(ballMove)
  if(!ball.move(ctx)) {
    window.cancelAnimationFrame(timer)
  }
}

生成的效果
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值