第 3 章 零基础 JavaScript 够用版

5 阅读6分钟

这一章只学习本教程需要用到的 JavaScript。

你不需要现在就掌握所有语法,也不需要背诵一长串术语。先把代码读懂、改动、运行起来,遇到新问题再补充新知识。

3.1 变量:给数据取名字

变量可以理解成一个有名字的盒子,用来保存数据。

let score = 0
let playerName = '小猫'
const maxLife = 3

这里有三个变量:

  • score 保存分数;
  • playerName 保存玩家名字;
  • maxLife 保存最大生命值。

let 表示这个变量之后可能改变,const 表示这个变量创建后不再重新指向别的值。

游戏运行时,分数会增加,所以使用 let:

score = score + 10

也可以写成:

score += 10

3.2 数字、文字和布尔值

游戏中经常会用到三种数据:

let speed = 5
let title = '星际小猫'
let isGameOver = false
  • 数字用于位置、速度、分数和生命;
  • 字符串用于名称、提示文字和按钮文字;
  • 布尔值只有 true 和 false,适合表示开关状态。

例如:

if (isGameOver) {
  console.log('游戏结束')
}

3.3 条件判断:让程序做选择

条件判断的基本形式是:

if (score >= 100) {
  console.log('达到奖励分数')
} else {
  console.log('继续努力')
}

程序会先判断 score >= 100 是否成立。如果成立,执行第一段代码;否则执行 else 中的代码。

游戏中经常需要做类似判断:

if (player.x < 0) {
  player.x = 0
}

这段代码可以防止玩家跑到屏幕左边之外。

3.4 循环:重复处理多个对象

假设屏幕上有很多敌人,我们不希望重复写很多遍代码。可以把敌人放进数组,再用循环逐个处理。

const enemies = ['红色敌机', '蓝色敌机', '绿色敌机']

for (const enemy of enemies) {
  console.log(enemy)
}

这段代码会依次打印三个敌人的名字。

游戏里更常见的写法是:

for (const enemy of enemies) {
  enemy.y += enemy.speed
}

意思是:让每一个敌人都向下移动。

3.5 函数:把一组动作打包

函数是一组可以重复调用的代码。

function addScore(points) {
  score += points
}

addScore(10)
addScore(20)

points 是参数。调用 addScore(10) 时,函数会把 points 当成 10。

函数也可以返回结果:

function isOutsideScreen(x, width) {
  return x < 0 || x > width
}

const outside = isOutsideScreen(500, 375)

return 会把计算结果交还给调用函数的地方。

把代码拆成函数有两个好处:

  • 每个函数负责一件事,阅读更容易;
  • 同一段逻辑可以多次使用。

3.6 对象:描述一个游戏角色

如果只用一堆变量描述玩家,很快会变得混乱:

let playerX = 100
let playerY = 500
let playerSpeed = 4
let playerLife = 3

可以把相关数据放到同一个对象中:

const player = {
  x: 100,
  y: 500,
  speed: 4,
  life: 3
}

读取和修改对象属性:

player.x += player.speed
player.life -= 1

对象非常适合表示游戏中的实体。玩家、敌人、子弹和道具,都可以拥有自己的位置、速度和状态。

3.7 数组:保存一组对象

const bullets = []

bullets.push({
  x: 100,
  y: 500,
  speed: 8
})

bullets 是一个数组,里面可以放很多子弹对象。

之后可以遍历它们:

for (const bullet of bullets) {
  bullet.y -= bullet.speed
}

在后面的章节中,我们会学习怎样删除已经飞出屏幕的子弹。

3.8 类:批量创建结构相同的对象

当玩家、敌人和子弹需要更多属性时,可以使用 class:

class Bullet {
  constructor(x, y) {
    this.x = x
    this.y = y
    this.speed = 8
  }

  update() {
    this.y -= this.speed
  }
}

const bullet = new Bullet(100, 500)
bullet.update()

constructor 会在创建对象时运行。this 表示当前创建出来的这个对象。

你可以暂时把类理解成一个模具:

  • class Bullet 是模具;
  • new Bullet(100, 500) 是用模具做出一颗子弹;
  • this.x 和 this.y 是每颗子弹自己的数据。

现在不需要深入理解原型链、继承和装饰器。先会创建、更新和使用游戏对象就够了。

3.9 调试:让程序告诉你发生了什么

最简单的调试工具是 console.log:

console.log('当前分数:', score)
console.log('玩家位置:', player.x, player.y)

运行游戏后,在开发者工具的 Console 面板里可以看到这些内容。

当画面不对时,可以暂时打印:

  • 某个变量当前是什么值;
  • 某个函数是否被调用;
  • 某个判断是否成立;
  • 某个数组里到底有多少个对象。

例如:

console.log('敌人数量:', enemies.length)

如果你认为敌人应该有 3 个,但日志显示是 0,就说明问题出在敌人创建或添加的地方,而不是绘制代码。

3.10 阅读报错信息

看到报错时,优先关注三部分:

  1. 报错类型;
  2. 报错文件;
  3. 报错行号。

例如,xxx is not defined 通常表示你使用了一个还没有创建的变量。Cannot read properties of undefined 通常表示你想读取一个不存在对象的属性。

不要只看最后一句中文提示,也不要立刻把整段代码删掉。先找到报错行,再检查这一行使用的变量和函数。

常见坑

坑一:把 = 和 === 混在一起

= 是赋值,=== 是比较:

score = 100
score === 100

第一句是把分数改成 100,第二句是在判断分数是否等于 100。

坑二:变量名拼写不一致

playerSpeed、playerspeed 和 player_speed 是三个不同的名字。代码编辑器不会自动替你猜测想写哪个。

坑三:数组下标从 0 开始

const colors = ['red', 'blue', 'green']
console.log(colors[0])

打印出来的是 red,不是第二个元素。第一个元素的位置是 0。

坑四:函数没有调用

写出函数并不代表函数已经执行:

function startGame() {
  console.log('开始游戏')
}

startGame()

最后一行才是真正调用函数。

本章小结

本章学习了本教程最常用的 JavaScript 基础:变量、条件、循环、函数、对象、数组、类和调试。

这些知识已经足够支持我们开始写真正的小游戏。后面遇到新语法时,再结合具体功能学习,不需要现在一次性背完。

动手练习

完成下面三个小任务:

  1. 创建一个 player 对象,包含 x、y、speed 和 life 四个属性;
  2. 写一个 addScore 函数,让分数增加指定数值;
  3. 创建一个包含三个敌人的数组,用循环打印每个敌人的名字。

下一章预告

下一章我们会正式进入 Canvas:把背景、文字、图形和图片画到屏幕上,并让一个小方块真正动起来。