欢迎对编程有兴趣的读者加入微信群一起分享编程学习过程中得到的收获和遇到的问题,微信号:latiao-java,也可以加入我的免费知识星球向我提问:https://t.zsxq.com/Nrz7uNZ
项目实战:猜猜我是几?
大家还记得我们在上一篇文章中运行过的那个猜数字程序吗?
我们在这篇文章里就要真正的完成这个程序了。下面是整个程序的源代码,大家可以复制到控制台里先玩一下试试。
var n = Math.ceil(Math.random() * 100);
var input = prompt('猜猜我是几?');
for (; input != null;) {
if (n > input) {
alert('我大于' + input);
} else if (n < input) {
alert('我小于' + input);
} else {
alert('猜对啦!我是' + n);
break;
}
input = prompt('猜猜我是几?');
}
但是罗马不是一天建成的,我们要先从一个简单的版本入手,一点一点地完成这个项目。
把我们猜的数字也放到变量里
在上一篇文章的最后,我们完成的是下面这样的一个程序。相信大家用这个程序猜数字的时候感觉最麻烦的一点就是每次猜一个新的数字就必须要把下面这个代码里的所有42都修改成自己想猜的数字,非常的麻烦。
if (n > 42) {
alert("我大于42");
} else if (n < 42) {
alert("我小于42");
} else {
alert("猜对啦!我是42");
}
上一篇文章中我们学到的第一个核心概念就是“变量”。我们可以把变量当做一个盒子,然后把变量的值当做盒子里的纸条,这个纸条上可以写数字也可以写一句话(也就是我们说的“字符串”)。变量的值是可以改变的,只要把变量的值修改了以后,所有使用变量的地方获取到的值就都会发生变化了。
所以在这段代码里,我们就可以用变量来代替所有使用到数字“42”的地方,用一个变量来代表我们每次猜的数字:
var input = 42;
if (n > input) {
alert("我大于" + input);
} else if (n < input) {
alert("我小于" + input);
} else {
alert("猜对啦!我是" + input);
}
在上面这段代码里,我们不再直接把42写在程序里,而是把它放到了变量input
中。在英语里,input
的意思就是输入,在这里也就是我们每次猜的数字了。
然后在下面的if语句里面,我们就直接拿变量n和变量input进行比较了,程序在运行时会分别取出两个变量里保存的数字,然后比较这两个数字得到结果。在这里如果变量n里保存的也是42,那么在第一个if判断条件里就是比较42 > 42
,结果就是false
,程序不会进入到第一个if语句后面的大括号{}
里。
然后在alert提示里,我们把input
的值拼接到了对应的字符串后面,比如alert("我大于" + input)
就是把变量input里保存的数字拼到“我大于”这三个字后面,变成“我大于42”,然后再通过alert()
函数弹出对话框。
大家可以把上面的整段代码复制到控制台里执行看看,最后的输出结果是什么。
然后我们再把input的值换成其他数字试试,看看最后能不能猜对变量n
中保存的随机数到底是多少。
循环
但是多猜几次之后感觉这样还是有点麻烦,每次都要改一下变量input的值,然后再把代码复制到控制台里运行。我们能不能让程序像我们在这篇文章一开始玩的那个成品一样,自动拿到我们猜的数字然后告诉我们结果,之后又不断重复这个过程,直到我们猜对了为止呢?
这就要用到我们接下来要学的一个知识点了——for循环。
for循环
for循环代码的基本格式是这样的:
for (1.初始化; 2.跳出条件; 4.循环后处理) {
3.重复执行的代码
}
- 首先会执行“1.初始化”,里面可以是任意的代码,这一段比较特殊,只会在整个循环开始前被执行一次;
- 然后程序会执行“2.跳出条件”,这段代码的执行结果是
true
或者false
- 如果结果是
true
,那么就继续执行第三步 - 如果结果是
false
,那么就跳出循环不再执行循环中的代码了
- 如果结果是
- 第三步是用大括号
{}
包围的代码块了,很多读者现在应该很熟悉了,这里和if后面大括号{}
里的代码块一样,可以是任意多行的代码 - 最后一步是“4.循环后处理”,这段代码会在大括号
{}
里的代码块执行完之后再被执行- 这一步执行完以后就会重新开始执行第二步“2.跳出条件”,如果条件计算结果是
true
那么循环就会继续,如果结果是false
,循环就结束了。
- 这一步执行完以后就会重新开始执行第二步“2.跳出条件”,如果条件计算结果是
从上面的执行过程我们可以看到,除了第一步,后面的第二、三、四步都是会被重复执行好多次的。直到“2.跳出条件”的结果变成false
才会结束这样的重复执行。在编程里,这样会被重复执行的代码就被叫做“循环”。
下面我们先来写一个简单的循环代码,计算从1到100数字相加的总和:
var sum;
sum = 0;
for (var i = 1; i <= 100; i=i+1) {
sum = sum + i;
}
sum;
- 首先,我们创建了一个变量
sum
,在英语里,sum
就是总和的意思 - 然后我们把数字0保存到变量sum里
- 之后就到了我们的重头戏——for循环
- 第一步,代码会执行
var i = 1
,这里就是创建了一个变量i并且把数字1保存到了它里面。这段代码只会在循环刚开始被执行一次,后面就不会再重复执行了。 - 第二步,程序要判断变量i是否小于等于100,因为在每次执行的过程中变量i都会增加1,所以最后变量i超过数字100的时候我们就可以结束循环了
- 第三步,我们会把变量i当前的值加到变量sum上,所以循环代码会把从1开始的数字,一个一个加到变量sum上。而sum一开始的值是0,那么sum最后保存的值就是1到100的数字之和了
- 第四步,程序执行了
i=i+1
,这里就会把变量i里面保存的值增加1,这也就是第二步中为什么要判断变量i是不是超过了100的原因。因为每次执行第二三四步之后变量i都会增加1,所以变量i会从1开始,一路从2、3、4、5,增加到99、100,最后在超过100之后就退出循环了。
- 第一步,代码会执行
我们来模拟一下这个程序的执行过程就是:
- i = 1
- sum = sum + i(值为1),sum的值变为1
- i = i + 1,变量i的值变为2
-
i <= 100
为true
,继续执行 - sum = sum + i(值为2),sum的值变为3(1+2)
- i = i + 1,变量i的值变为3
-
i <= 100
为true
,继续执行 - ......
- i = i + 1,变量i的值变为100
-
i <= 100
为true
,继续执行 - sum = sum + i(值为100),sum的值变为5050(1到100的数字之和)
- i = i + 1,变量i的值变为101
-
i <= 100
为false
,退出循环
因为sum上加上了从1到100的所有数字,所以最后变量sum里保存的就是从1到100所有数字的总和了。
把程序“猜猜我是几?”改为循环版本
在上一节中,我们已经把“猜猜我是几?”程序优化到下面这样了:
var input = 42;
if (n > input) {
alert("我大于" + input);
} else if (n < input) {
alert("我小于" + input);
} else {
alert("猜对啦!我是" + input);
}
从对这段代码的使用中我们能知道,只要解决两个问题就可以让它自动运行起来:
- 可以重复地循环执行
- 可以在每次执行时获取我们想猜的数字
首先,我们试试把前面的for循环代码套在上面这段代码外面,让这段代码可以重复执行3次:
for (var i = 0; i < 3; i = i + 1) {
if (n > input) {
alert("我大于" + input);
} else if (n < input) {
alert("我小于" + input);
} else {
alert("猜对啦!我是" + input);
}
}
在上面的这段代码里,for语句后面大括号里的代码和前面的那段代码是一模一样的。区别只是外面多套了一层for (var i = 0; i < 3; i = i + 1)
,所以这段代码会重复执行三次。运行这段代码,我们发现,同样的结果弹出了三次,我们并不能在每次执行时猜一个新的数字。
为了能在程序运行时输入我们想猜的数字,我们要用上一个特殊的函数prompt()
。我们可以在控制台里运行一下prompt('猜猜我是几?');
试一试。
首先,我们会看到一个可以输入数字的对话框。然后我们输入42再点击一下“确定”看看。
在上面的截图里我们可以看到,点击确定以后,对话框消失了,而且函数prompt('猜猜我是几?');
的运行结果就是我们输入的42。所以只要我们把prompt()
的结果保存到变量input里,我们就可以在每次比较数字之前先输入我们要猜的数字了。就像下面这样:
input = prompt('猜猜我是几?');
如果你不太理解什么是函数,prompt的运行结果又是怎么回事的话,没有关系,只要复制上面这段代码,然后知道它的意思就是把我们在对话框里输入的数字放到变量input里就可以了。
然后我们可以把程序修改成下面这样来在每次循环开始时输入要猜的数字:
var n = Math.ceil(Math.random() * 100);
var input;
input = prompt('猜猜我是几?');
for (var i = 0; i < 5; i+=1) {
if (n > input) {
alert('我大于' + input);
} else if (n < input) {
alert('我小于' + input);
} else {
alert('猜对啦!我是' + n);
}
// 下一次循环时要输入新的猜的数字
input = prompt('猜猜我是几?');
}
上面这段代码里,我们可以在循环的每一次执行中输入我们要猜的数字了。主要的区别就是在循环开始之前和每次循环执行之后添加了一句input = prompt('猜猜我是几?');
来获取我们下一轮要猜的数字,然后等待程序告诉我们结果是大于、小于还是猜对了。
跳出循环
但是我们多玩几次就会发现,就算我们猜对了,这个程序也还是会让我们继续猜。这很不合理啊,我们既然都已经猜对了,那就应该结束了才对。
这就可以用上一个新的语句break;
了,如果在循环里执行一个break;
,那么程序就会从循环里直接跳出来。
比如我们前面的1到100的求和程序,如果我们在i增加到50的时候就break会怎么样?
var sum;
sum = 0;
for (var i = 1; i <= 100; i=i+1) {
sum = sum + i;
if (i == 50) {
break;
}
}
sum;
我们复制到控制台里运行,最后的结果只有1275,也就是1到50的总和。也就是说,这个循环执行到i到达50的时候就退出了,所以变量sum里只累加到了50。
在我们的项目“猜猜我是几?”里,我们可以在猜对之后就执行一个break;
。然后代码中的循环就会退出,现在我们猜对之后就不会再弹出对话框让我们继续猜了。
var n = Math.ceil(Math.random() * 100);
var input;
input = prompt('猜猜我是几?');
for (var i = 0; i < 5; i=i+1) {
if (n > input) {
alert('我大于' + input);
} else if (n < input) {
alert('我小于' + input);
} else {
alert('猜对啦!我是' + n);
// 猜对之后就退出了!!!
break;
}
input = prompt('猜猜我是几?');
}
我不想玩了!
但是如果我们不想玩了,然后点击取消会发生什么呢?
弹出了一个写着“我大于null”的对话框,而且点击“确定”之后又弹出了“猜猜我是几?”。在这种情况下,我们希望看到的结果应该是程序在取消之后就退出了才对。
那么我们要考虑的第一个问题就是程序怎么知道我们点了“取消”按钮?
从上面的截图里,我们看到对话框里的文字是“我大于null”,本来应该显示变量input的位置显示的是“null”。所以如果我们点击了“取消”按钮,那么prompt()
函数的结果就会是null,而且这个值会被放到变量input里。既然这样,那么我们只要判断变量input的值是不是null就可以知道“取消”按钮有没有被点击了。
所以,我们就可以用if (input == null)
来判断有没有按下“取消”按钮:
var input = prompt('猜猜我是几?');
for (var i = 0; i < 3; i=i+1) {
// 如果点击了“取消”按钮,那么就直接跳出循环
if (input == null) {
break;
}
if (n > input) {
alert('我大于' + input);
} else if (n < input) {
alert('我小于' + input);
} else {
alert('猜对啦!我是' + n);
break;
}
input = prompt('猜猜我是几?');
}
不过这个地方我们能把这个if语句化简到for语句的跳出条件里,因为for语句后面小括号中的跳出条件其实也是一个结果为true
或false
的代码。如果我们可以把i < 3
和input == null
两个条件结合成一个结果为true
和false
的代码,那就不用在下面另外写if (input == null)
了。
我们可以用一个逻辑关系操作符&&
(且)来组合多个不同的条件,a < 1 && b < 2
的意思就是要a < 1
和b < 2
的结果都为true
的情况下整个a < 1 && b < 2
的结果才是true
,否则就是false
。口头来解释就是如果要“a小于1而且b小于2”,那么两个条件都要是对的,只要其中有一个不对,那么这句话就是错的了。
在这里,我们可以把if (input == null)
移到上面的for循环跳出条件里,变成i < 3 && input != null
:
var input = prompt('猜猜我是几?');
for (var i = 0; i < 3 && input != null; i=i+1) {
if (n > input) {
alert('我大于' + input);
} else if (n < input) {
alert('我小于' + input);
} else {
alert('猜对啦!我是' + n);
break;
}
input = prompt('猜猜我是几?');
}
为什么这里用的是input != null
呢?因为跳出条件结果为true
时循环会继续执行,而在变量input等于null时我们会跳出循环(break)。所以反过来就是如果变量input不等于null,那我们就继续执行循环。因此这里在跳出条件中用的是input != null
。
其他的逻辑操作符还有:
-
||
或操作,只要左右的两个条件中有一个是对的,那整个语句就是对的,也就是结果为true
- 比如
a < 1 || b < 2
,那么只要a小于1或者b小于2,那么这句代码的结果就是true
- 比如
-
!
非操作,这就是把后面跟着的条件最后的结果反过来- 比如如果
a < 1
是true,那么!(a < 1)
就是false
- 比如如果
我还没猜对呢!
现在这个程序还有一点点美中不足,就是如果我们在3次机会里没有猜到数字是多少,那就要重新把代码复制到控制台里才能继续猜了。但是我们希望这个程序能够一直运行到我们猜对了为止。
要做到这一点其实很容易,因为这个程序只能猜三次的限制其实就在for语句的跳出条件里,如果我们把条件i < 3
去掉,那么循环就不会因为次数到达3次而跳出了。同时,如果不用判断i < 3
,那么我们也没有其他地方需要用到变量i了,那就可以把for语句后面的小括号()
里的第一部分和第三部分都删掉了。那么最后for语句就可以被我们改成for (; input != null; )
。
改完后的完整程序就是下面这样:
var n = Math.ceil(Math.random() * 100);
var input = prompt('猜猜我是几?');
for (; input != null; ) {
if (n > input) {
alert('我大于' + input);
} else if (n < input) {
alert('我小于' + input);
} else {
alert('猜对啦!我是' + n);
break;
}
input = prompt('猜猜我是几?');
}
还可以做得更好
我们实现的程序已经能自动地完成一轮猜数字的游戏了,但是如果我们想要再玩下一轮就又要把代码重新复制到控制台执行一次了,感觉还是有点麻烦。你能帮我们实现一个可以连续玩好几轮版本的“猜猜我是几?”程序吗?
要求:
- 在一个随机数被猜中以后可以重新生成一个新的随机数继续下一轮游戏
- 在输入数字的对话框中点击“取消”按钮可以结束程序