// 循环:while 和 for
// 我们经常需要重复执行一些操作。
// 例如,我们需要将列表中的商品逐个输出,或者运行相同的代码将数字 1 到 10 逐个输出。
// 循环 是一种重复运行同一代码的方法。
// “while” 循环
// while (1) {
//
// }
// 当 condition 为真时,执行循环体的 code。
let i =0;
while (i <3) {// 依次显示 0、1 和 2
alert(i );
i++;
}
// 循环体的单次执行叫作 一次迭代。上面示例中的循环进行了三次迭代。
// 如果上述示例中没有 i++,那么循环(理论上)会永远重复执行下去。实际上,浏览器提供了阻止这种循环的方法,我们可以通过终止进程,来停掉服务器端的 JavaScript。
// 任何表达式或变量都可以是循环条件,而不仅仅是比较。在 while 中的循环条件会被计算,计算结果会被转化为布尔值。
// 例如,while (i != 0) 可简写为 while (i):
let i =3;
while (i) {// 当 i 变成 0 时,条件为假,循环终止
alert(i );
i--;
}
// “do…while” 循环
do {
// 循环体
}while (condition);
// 循环首先执行循环体,然后检查条件,当条件为真时,重复执行循环体。
let i =0;
do {
alert(i );
i++;
}while (i <3);
// 这种形式的语法很少使用,除非你希望不管条件是否为真,循环体 至少执行一次。通常我们更倾向于使用另一个形式:while(…) {…}。
// “for” 循环
for (begin; condition; step) {
// ……循环体……
}
for (let i =0; i <3; i++) {// 结果为 0、1、2
alert(i);
}
// 我们逐个部分分析 for 循环:
// 语句段
// begin let i = 0 进入循环时执行一次。
// condition i < 3 在每次循环迭代之前检查,如果为 false,停止循环。
// body(循环体) alert(i) 条件为真时,重复运行。
// step i++ 在每次循环体迭代后执行。
// 内联变量声明
// 这里“计数”变量 i 是在循环中声明的。这叫做“内联”变量声明。这样的变量只在循环中可见。
for (let i =0; i <3; i++) {
alert(i); // 0, 1, 2
}
alert(i); // 错误,没有这个变量。
// 除了定义一个变量,我们也可以使用现有的变量:
let i =0;
for (i =0; i <3; i++) {// 使用现有的变量
alert(i); // 0, 1, 2
}
alert(i); //3,可见,因为是在循环之外声明的
// 省略语句段
// for 循环的任何语句段都可以被省略。
// 例如,如果我们在循环开始时不需要做任何事,我们就可以省略 begin 语句段。
// 就像这样:
let i =0; // 我们已经声明了 i 并对它进行了赋值
for (; i <3; i++) {// 不再需要 "begin" 语句段
alert(i ); // 0, 1, 2
}
// 我们也可以移除 step 语句段:
let i =0;
for (; i <3;) {
alert(i++ );
}
for (;;) {
// 无限循环
}
// 跳出循环
// 通常条件为假时,循环会终止。
// 但我们随时都可以使用 break 指令强制退出。
// 例如,下面这个循环要求用户输入一系列数字,在输入的内容不是数字时“终止”循环。
let sum =0;
while (true) {
let value = +prompt("Enter a number", '');
if (!value)break; // (*)
sum += value;
}
alert('Sum: ' +sum );
// 如果用户输入空行或取消输入,在 (*) 行的 break 指令会被激活。它立刻终止循环,将控制权传递给循环后的第一行,即,alert。
// 根据需要,"无限循环 + break" 的组合非常适用于不必在循环开始/结束时检查条件,但需要在中间甚至是主体的多个位置进行条件检查的情况。
// 继续下一次迭代
// continue 指令是 break 的“轻量版”。它不会停掉整个循环。而是停止当前这一次迭代,并强制启动新一轮循环(如果条件允许的话)。
// 如果我们完成了当前的迭代,并且希望继续执行下一次迭代,我们就可以使用它。
// 下面这个循环使用 continue 来只输出奇数:
if (let i =0, i <10, i++) {
if (i %2 ==0)continue;
}
// 对于偶数的 i 值,continue 指令会停止本次循环的继续执行,将控制权传递给下一次 for 循环的迭代(使用下一个数字)。因此 alert 仅被奇数值调用。
// continue 指令利于减少嵌套
// 显示奇数的循环可以像下面这样
for (let i =0; i <10; i++) {
if (i %2) {
alert( i );
}
}
// 禁止 break/continue 在 ‘?’ 的右边
// break/continue 标签 暂时未用到
// 有时候我们需要从一次从多层嵌套的循环中跳出来。
// 例如,下述代码中我们的循环使用了 i 和 j,从 (0,0) 到 (3,3) 提示坐标 (i, j):
outer:for (let i =0; i <3; i++) {
for (let j =0; j <3; j++) {
let input =prompt(`Value at coords (${i},${j})`, '');
// 如果是空字符串或被取消,则中断并跳出这两个循环。
if (!input)break outer; // (*)
// 用得到的值做些事……
}
}
alert('Done!');
我们学习了三种循环:
// while —— 每次迭代之前都要检查条件。
// do..while —— 每次迭代后都要检查条件。
// for (;;) —— 每次迭代之前都要检查条件,可以使用其他设置。
// 通常使用 while(true) 来构造“无限”循环。这样的循环和其他循环一样,都可以通过 break 指令来终止。
// 如果我们不想在当前迭代中做任何事,并且想要转移至下一次迭代,那么可以使用 continue 指令。
// break/continue 支持循环前的标签。标签是 break/continue 跳出嵌套循环以转到外部的唯一方法。
// 大于 1 且不能被除了 1 和它本身以外的任何数整除的整数叫做素数。
// 换句话说,n > 1 且不能被 1 和 n 以外的任何数整除的整数,被称为素数。
// 例如,5 是素数,因为它不能被 2、3 和 4 整除,会产生余数。
// 写一个可以输出 2 到 n 之间的所有素数的代码。
// 当 n = 10,结果输出 2、3、5、7。
// P.S. 代码应适用于任何 n,而不是对任何固定值进行硬性调整。
// 对于间隔中的每个 i {
// 检查在 1~i 之间,是否有 i 的除数
// 如果有 => 这个 i 不是素数
// 如果没有 => 这个 i 是素数,输出出来
// }
// let n = 10;
//
// nextPrime:
// for (let i = 2; i <= n; i++) { // 对每个自然数 i
//
// for (let j = 2; j < i; j++) { // 寻找一个除数……
// if (i % j == 0) continue nextPrime; // 不是素数,则继续检查下一个
// }
//
// alert( i ); // 输出素数
// }
// "switch" 语句
// switch 语句可以替代多个 if 判断。
let x ="xxx"
switch (x) {
case "xxx":
alert(x)
break
case "xxx":
alert(x)
break
default:
alert(x)
}
//分组
let a =3;
switch (a) {
case 4:
alert('Right!');
break;
case 3:// (*) 下面这两个 case 被分在一组
case 5:
alert('Wrong!');
alert("Why don't you take a math class?");
break;
default:
alert('The result is strange. Really.');
}
// 类型很关键
// 强调一下,这里的相等是严格相等。被比较的值必须是相同的类型才能进行匹配。
// 函数
function showMessage() {
alert('xxxxx')
}
showMessage()
function showMessageWtih(par1,par2) {
alert(par1 + par2)
}
showMessageWtih("x","sss")
// 全局变量
// 任何函数之外声明的变量,例如上述代码中的外部变量 userName,都被称为 全局 变量。
// 全局变量在任意函数中都是可见的(除非被局部变量遮蔽)。
// 减少全局变量的使用是一种很好的做法。现代的代码有很少甚至没有全局变量。大多数变量存在于它们的函数中。但是有时候,全局变量能够用于存储项目级别的数据。
// 参数
// 我们可以通过参数将任意数据传递给函数。
function showMessage(from, text) {// 参数:from 和 text
alert(from +': ' + text);
}
showMessage('Ann', 'Hello!'); // Ann: Hello! (*)
showMessage('Ann', "What's up?"); // Ann: What's up? (**)
// 换一种方式,我们把这些术语搞清楚:
// 参数(parameter)是函数声明中括号内列出的变量(它是函数声明时的术语)。
// 参数(argument)是调用函数时传递给函数的值(它是函数调用时的术语)。
// 默认值
// 如果一个函数被调用,但有参数(argument)未被提供,那么相应的值就会变成 undefined。
// 例如,之前提到的函数 showMessage(from, text) 可以只使用一个参数(argument)调用:
// showMessage("Ann");
// 那不是错误,这样调用将输出 "*Ann*: undefined"。因为参数 text 的值未被传递,所以变成了 undefined。
// 我们可以使用 = 为函数声明中的参数指定所谓的“默认”(如果对应参数的值未被传递则使用)值:
// function showMessage(from, text = "no text given") {
// alert( from + ": " + text );
// }
// showMessage("Ann");
// 现在如果 text 参数未被传递,它将会得到值 "no text given"。
// 这里 "no text given" 是一个字符串,但它可以是更复杂的表达式,并且只会在缺少参数时才会被计算和分配。所以,这也是可能的:
function showMessage(from, text = anotherFunction()) {
// anotherFunction() 仅在没有给定 text 时执行
// 其运行结果将成为 text 的值
}
// 在 JavaScript 中,每次函数在没带个别参数的情况下被调用,默认参数会被计算出来。
// 在上面的例子中,如果传递了参数 text,那么 anotherFunction() 就不会被调用。
// 如果没传递参数 text,那么 anotherFunction() 就会被调用。
// 后备的默认参数
// 有些时候,将参数默认值的设置放在函数执行(相较更后期)而不是函数声明时,也行得通。
// 我们可以通过将参数与 undefined 进行比较,来检查该参数是否在函数执行期间被传递进来:
function showMessage(text) {
// ...
if (text ===undefined) {// 如果参数未被传递进来
text ='empty message';
}
alert(text);
}
showMessage(); // empty message
function showCount(count) {
// 如果 count 为 undefined 或 null,则提示 "unknown"
alert(count ??"unknown");
}
showCount(0); // 0
showCount(null); // unknown
showCount(); // unknown
// 返回值
function sum(a,b) {
return a + b
}
alert(sum(1,2))
// 指令 return 可以在函数的任意位置。当执行到达时,函数停止,并将值返回给调用代码(分配给上述代码中的 result)。
function checkAge(age) {
if (age >=18) {
return true;
}else {
return confirm('Got a permission from the parents?');
}
}
let age =prompt('How old are you?', 18);
if (checkAge(age) ) {
alert('Access granted' );
}else {
alert('Access denied' );
}
function showMovie(age) {
if ( !checkAge(age) ) {
return;
}
alert("Showing you the movie" ); // (*)
// ...
}
// 函数命名
// 函数就是行为(action)。所以它们的名字通常是动词。它应该简短且尽可能准确地描述函数的作用。这样读代码的人就能清楚地知道这个函数的功能。
// 一种普遍的做法是用动词前缀来开始一个函数,这个前缀模糊地描述了这个行为。团队内部必须就前缀的含义达成一致。
// 例如,以 "show" 开头的函数通常会显示某些内容。
// 函数以 XX 开始……
// "get…" —— 返回一个值,
// "calc…" —— 计算某些内容,
// "create…" —— 创建某些内容,
// "check…" —— 检查某些内容并返回 boolean 值,等。
// showMessage(..) // 显示信息
// getAge(..) // 返回 age(gets it somehow)
// calcSum(..) // 计算求和并返回结果
// createForm(..) // 创建表单(通常会返回它)
// checkPermission(..) // 检查权限并返回 true/false
// 作为参数传递给函数的值,会被复制到函数的局部变量。
// 函数可以访问外部变量。但它只能从内到外起作用。函数外部的代码看不到函数内的局部变量。
// 函数可以返回值。如果没有返回值,则其返回的结果是 undefined。
// 为了使代码简洁易懂,建议在函数中主要使用局部变量和参数,而不是外部变量。
// 与不获取参数但将修改外部变量作为副作用的函数相比,获取参数、使用参数并返回结果的函数更容易理解。
// 函数命名:
// 函数名应该清楚地描述函数的功能。当我们在代码中看到一个函数调用时,一个好的函数名能够让我们马上知道这个函数的功能是什么,会返回什么。
// 一个函数是一个行为,所以函数名通常是动词。
// 目前有许多优秀的函数名前缀,如 create…、show…、get…、check… 等等。使用它们来提示函数的作用吧。
// 函数是脚本的主要构建块。现在我们已经介绍了基本知识,现在我们就可以开始创建和使用函数了。但这只是学习和使用函数的开始。我们将继续学习更多函数的相关知识,更深入地研究它们的先进特征。
function checkAge(age) {
return (age >18) ?true :confirm('Did parents allow you?');
}
使用或运算符 ||(最短的变体):
function checkAge(age) {
return (age >18) ||confirm('Did parents allow you?');
}
function pow(x, n) {
let result = x;
for (let i =1; i < n; i++) {
result *= x;
}
return result;
}
let x =prompt("x?", '');
let n =prompt("n?", '');
if (n <1) {
alert(`Power ${n} is not supported, use a positive integer`);
}else {
alert(pow(x, n) );
}