就拿写的游戏的案例举例
游戏中的基类
function Sprite(posX=0, posY=0, width=10, height=10) {
this.posX = posX
this.posY = posY
this.width = width
this.height = height
this.color = ['red', 'blue']
}
Sprite.prototype = {
render() {
console.log('this is a render function')
},
update() {
console.log('this is a update function')
}
}
// 功能实现
function Ball(vx, vy) {
this.vx = vx
this.vy = vy
}
Ball.prototype = new Sprite() // 缺陷2、3
// 使用
let ball = new Ball(0.1, 0.1) // 缺陷1
ball.posX = 10
ball.posY = 10
ball.width = 50
ball.height = 50
ball.color.push('black') // 缺陷3
let ball2 = new Ball(0.2, 0.2)
// 缺陷:
// 1、实例化子类时无法向父类构造函数传参,期望是new Ball(10, 10, 50, 50, 0.1, 0.1)
// 2、单一继承
// 3、子类共享父类实例属性
// 功能实现
function Ball(posX, posY, width, height, vx, vy) {
Sprite.call(this, posX, posY, width, height)
this.vx = vx
this.vy = vy
}
// 使用
let ball = new Ball(100, 100, 50, 50, 0.1, 0.1)
// 缺陷:
// 1、子类无法继承父类原型上的属性、方法
// 2、无法实现构造函数的复用,每次实例化都会重新调用
// 3、每个实例都有父类构造函数的副本,臃肿
// 功能实现
function Ball(posX, posY, width, height, vx, vy) {
Sprite.call(this, posX, posY, width, height) // 第一次
this.vx = vx
this.vy = vy
}
Ball.prototype = new Sprite() // 第二次
// 使用
let ball = new Ball(100, 100, 50, 50, 0.1, 0.1)
ball.color.push('black')
et ball2 = new Ball(200, 200, 20, 20, 0.2, 0.2)
// 缺陷:
// 调用了两次构造函数
// 功能实现
Ball.prototype = Sprite.prototype
Ball.prototype.canMove = true
function Ball(vx, vy) {
this.vx = vx
this.vy = vy
}
// 缺陷:
// 1、实例化子类时无法向父类构造函数传参
// 2、单一继承
// 3、修改子类原型会修改父类原型
// 4、子类无法继承父类构造函数的属性
// 功能实现
Ball.prototype = Object.create(Sprite.prototype)
Ball.prototype.constructor = Ball
Ball.prototype.canMove = true
function Ball(posX, posY, width, height, vx, vy) {
Sprite.call(this, posX, posY, width, height)
this.vx = vx
this.vy = vy
}
// 使用
let ball = new Ball(100, 100, 50, 50, 0.1, 0.1)
ball.color.push('black')
let ball2 = new Ball(200, 200, 20, 20, 0.2, 0.2)
// 功能实现
class Sprite {
constructor(posX, posY, width, height) {
this.posX = posX
this.posY = posY
this.width = width
this.height = height
}
render() {
console.log('this is a render function')
}
update() {
console.log('this is a update function')
}
}
class Ball extends Sprite {
constructor(vx, vy, ...args) {
super(...args)
this.vx = vx
this.vy = vy
}
}
个人理解
-
如果不使用原型,就会有不能复用、每个子类实例都有父类的问题
-
直接给子类原型赋值就会有单一继承的问题
-
父类原型直接给子类原型就会造成修改子类原型也同时修改了父类原型
-
只使用父类原型就会有子类不能继承父类构造函数属性的问题
-
如果不使用call / apply,传参问题就不能解决
-
call / apply不仅可以解决传参问题,还可以解决单一继承问题
-
原型就是为了解决一些公共属性和方法
就拿写的游戏的案例举例
游戏中的基类
个人理解
如果不使用原型,就会有不能复用、每个子类实例都有父类的问题
直接给子类原型赋值就会有单一继承的问题
父类原型直接给子类原型就会造成修改子类原型也同时修改了父类原型
只使用父类原型就会有子类不能继承父类构造函数属性的问题
如果不使用call / apply,传参问题就不能解决
call / apply不仅可以解决传参问题,还可以解决单一继承问题
原型就是为了解决一些公共属性和方法