4.2.2.4 JS 速成:分支、循环结构与函数
INFO
本文按照 Mozilla 贡献者基于 CC-BY-SA 2.5 协议发布的以下文章改编:
- https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Control_flow_and_error_handling
- https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Building_blocks/conditionals
- https://developer.mozilla.org/zh-CN/docs/learn/JavaScript/Building_blocks/Looping_code
- https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Loops_and_iteration
- https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Building_blocks/Functions
- https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Building_blocks/Build_your_own_function
- https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Functions
布尔值
在正式开始分支结构之前,我们要讲下布尔值 (Boolean),这是进行条件判断的基础,分支和循环结构都要用到。
布尔值也是一种类型,和我们之前讲过的数字和字符串一样。不同的是它只有两个值: true
和 false
。分别对应 "真" 和 "假"。
> true;
true
> false;
false
>
2
3
4
5
一般情况下我们也不会这样用布尔值,而是采用一些比较运算和逻辑运算来得到,就像下面这样。
> 1 === 1; //1 和 1 相等,正确,是 true
true
> 1 === 0; //1 和 0 不相等
false
> true && true; // And 运算符
true
> true && false; // 必须两个都是 true 才是 true
false
>
2
3
4
5
6
7
8
9
比较运算
比较运算符是一个二元运算符,意思是左右两边分别需要一个运算数,就像之前我们讲过的四则运算一样。不过比较运算的结果不是普通的数字,而是一个布尔值,即 true
和 false
。
> 1 + 2; // 四则运算,结果是数字
3
> 1 < 2; // 比较运算的结果是布尔值
true
> 1 > 2; // 1 小于 2 是假命题,所以 false
false
>
2
3
4
5
6
7
通过这里,我们可以很明显的看出,比较运算有这两个特点:
- 如果表达式成立,那么值为 true
- 如果表达式不成立,那么值为 false
和数学类似,我们有大于,小于,等于这些比较运算符。不同的是,我们的大于等于和小于等于是 >=
和 <=
,因为键盘打不出来数学里那个。而等于一般使用 ===
,这样不会和赋值 =
混淆。常用的比较运算符有这些:
运算符 | 描述 |
---|---|
等于 Equal (== ) | 如果两边操作数相等时返回true。 |
不等于 Not equal (!= ) | 如果两边操作数不相等时返回true |
全等 Strict equal (=== ) | 两边操作数相等且类型相同时返回true。 |
不全等 Strict not equal (!== ) | 两边操作数不相等或类型不同时返回true。 |
大于 Greater than (> ) | 左边的操作数大于右边的操作数返回true |
大于等于 Greater than or equal (>= ) | 左边的操作数大于或等于右边的操作数返回true |
小于 Less than (< ) | 左边的操作数小于右边的操作数返回true |
小于等于 Less than or equal (<= ) | 左边的操作数小于或等于右边的操作数返回true |
下面是一些示例,可以看到它们和预期的结果是符合的:
> 1 + 2 >= 3;
true
> 569 < 123;
false
> 123 === 123;
true
> 2 * 3 === 3 + 3;
true
> 2 * 3 !== 3 + 3;
false
>
2
3
4
5
6
7
8
9
10
11
字符串也可以使用比较运算,它们之间的比较使用的是字典序,由于日常应用的时候基本只需要判断字符串之间是否相等,所以这里不详细提及字典序大小的比较。
> "aaa" === 'aaa'; // 相等
true
> "aaa" !== "bbb"; // 不相等
true
> 'aaa' > 'bbb'; // 字典序
false
> 'aaa' <= 'bbb';
true
>
2
3
4
5
6
7
8
9
你可能会注意到,上面我给出的示例只用了全等号 ( ===
) 和不全等号 ( !==
),而没有使用普通的等于和不等于。事实上我们提倡尽可能使用三个等号组成的全等号,因为它可以避免 JS 过于宽松的自动类型转换带来的一些容易造成错误的问题。看看下面的代码:
> "0" == 0; // 自动类型转换
true
> "0" === 0; // 禁止了自动类型转换
false
> 123 != "123";
false
> 123 !== "123";
true
2
3
4
5
6
7
8
这里等于号和不等号遇到了一些问题:它把一个字符串和一个数字进行了转换,然后认为它们是相等的。这可能会导致你的程序出现一些难以排查的错误,所以尽可能避免它们。
逻辑运算
在进行比较运算的时候,我们可能会需要将多个条件连起来,我们假设有这三个变量需要判断:
num1
大于 0 ,小于 100 。num2
可以被 2 整除或者可以被 3 整除。num3
既不被 5 整除,又不被 4 整除。
这就需要使用逻辑运算,将多个表达式组合起来,然后得到想要的结果。逻辑运算符有三种:
运算符 | 描述 | 示例 |
---|---|---|
与 ( && ) | 当两边表达式值都为 true 时为 true,否则为 false | true && true === true true && false === false |
或 ( || ) | 当两边表达式值有一个为 true 时为 true,否则为 false | true || false === true false || flase === false |
非 ( ! ) | 当后面的值是 true 是为 false,反之为 true | !true === false !false === true |
因此上面我们提到的例子可以这么写:
num1 > 0 && num1 < 100;
num2 % 2 === 0 || num2 % 3 === 0;
!(num3 % 5 === 0 || num3 % 4 === 0);
2
3
前面两个应该比较好理解,第三个为什么要带个括号呢?答案是运算符优先级问题。括号的作用是让括号里面的东西先算,这个学过数学都知道,放在这里也是一样的。如果我们去掉这个括号,就相当于下面这样:
!num3 % 5 === 0 || num3 % 4 === 0);
(!num3 % 5 === 0) || num3 % 4 === 0;
2
可以看到是左边的那个非运算符先算,再到后边的或运算符,就出错了。运算符优先级是这样:先算 !
,再算 &&
,最后算 ||
,同级别从左到右。如果写程序的时候个别优先级不了解,那就多打几个括号,最多丑点。
if 语句
前面讲了这么多逻辑,现在有请我们的主角出场吧:就是 if 语句 !有了它,你就可以在程序里面根据不同的条件,去做出一系列的决定。以下是它的伪代码表示:
if (一个表达式或者值) {
如果这个表达式结果是 true ,那么执行这里的内容;
可以有很多句;
} else {
如果值是 false ,那么执行这里的内容;
也可以有很多句;
}
2
3
4
5
6
7
关键字 if,并且后面跟随括号。
要测试的条件,放到括号里(通常是 “这个值大于另一个值吗” 或者 “这个值存在吗”)。这个条件会利用比较运算符(我们会在最后的模块中讨论)进行比较,并且返回 true 或者 false 。
一组花括号,在里面我们有一些代码 —— 可以是任何我们喜欢的代码,并且只会在条件语句返回 true 的时候运行。一般情况下前一个大括号和 if 在同一行,后一个单独一行,这样比较美观,也可以避免 JS 自动加入分号的机制造成的问题。
关键字 else 。
另一组花括号,在里面我们有一些代码 —— 可以是任何我们喜欢的代码,并且当条件语句返回值不是 true 的话,它才会运行。
这段代码真的非常易懂 —— 它说 “如果(if)条件(condition)返回 true,运行代码 A,否则(else)运行代码 B”
注意:你不一定需要 else 和第二个花括号 —— 下面的代码也是符合语法规则的:
if (一个表达式) {
如果这个表达式结果是 true ,那么执行这里的内容;
}
2
3
多个条件代码可以使用 else if
,你可以用它来进行多个条件的判断。
if (一个表达式) {
如果这个表达式结果是 true ,那么执行这里的内容;
} else if (一个表达式) {
如果前一个表达式是 false ,这个表达式结果是 true ,那么执行这里的内容;
} else if (一个表达式) {
如果前面的表达式都是 false ,这个表达式结果是 true ,那么执行这里的内容;
} else {
如果前面都是 false ,那么执行这里的内容;
}
2
3
4
5
6
7
8
9
if 语句也可以嵌套:
if (一个表达式) {
一些语句;
if (一个表达式){
一些语句;
}
一些语句;
}
2
3
4
5
6
7
有时候你可能会看到 if…else 语句没有写花括号,像下面的速记风格:
if (一个表达式) 一行代码;
else 一行代码;
2
这是完全有效的代码,但不建议这样使用 —— 因为如果有花括号进行代码切割的话,整体代码被切割为多行代码,更易读和易用。
switch 语句
在需要多重分支时可以使用基于一个数字或字符串的 switch 语句:
let x = 1;
switch (x) {
case 0:
console.log(0);
break;
case 1:
console.log(1);
break;
default:
console.log("default");
}
2
3
4
5
6
7
8
9
10
11
因为 switch 语句本质是跳到指定的标签开始执行,所以每个 case 结尾需要加上 break
语句用来退出,否则它会一直按顺序执行,运行到后面的代码,比如这样。
let x = 1;
switch (x) {
case 0:
console.log(0);
case 1:
console.log(1);
default:
console.log("default");
}
2
3
4
5
6
7
8
9
输出就变成了
1
default
2
一些例子
现在已经到了比较复杂的程序,所以我们就不再直接使用交互式解释器来运行我们的代码了。接下来的所有例子都会是一个单独的 JS 文件,你可以加到 HTML 里面通过浏览器运行,然后在控制台查看输出。
假设有一个年份,变量名叫做 year,现在要你判断它是否是闰年。
1582 年以来的置闰规则:
- 普通闰年:公历年份是 4 的倍数,且不是 100 的倍数的,为闰年(如 2004 年、2020 年等就是闰年)。
- 世纪闰年:公历年份是整百数的,必须是 400 的倍数才是闰年(如 1900 年不是闰年,2000 年是闰年)。
写成程序就是这样
// 实际情况需要配合输入,这个以后在讲 DOM 的时候会了解
let year = 2021;
// 先判断最特殊的
if (year % 4 !== 0) {
console.log("不是闰年");
} else if (year % 400 === 0) {
console.log("是闰年");
} else if (year % 100 === 0) {
console.log("不是闰年")
} else {
// 现在只能是被 4 整除的了
console.log("是闰年");
}
2
3
4
5
6
7
8
9
10
11
12
13
14
当然你也可以按照别的顺序进行判断。就像这个例子。只要答案是对的就可以。
if (year % 4 === 0) {
if (year % 100 === 0 && year % 400 !== 0) {
console.log("不是闰年");
} else {
console.log("是闰年");
}
} else {
console.log("不是闰年");
}
2
3
4
5
6
7
8
9
给定一个数组,然后从小到大排序数组里面的数字并输出,用空格分隔:
let myArray = [3, 1, 2];
if (myArray[0] > myArray[1]) {
let temp = myArray[0];
myArray[0] = myArray[1];
myArray[1] = temp;
}
if (myArray[1] > myArray[2]) {
let temp = myArray[1];
myArray[1] = myArray[2];
myArray[2] = temp;
}
if (myArray[0] > myArray[2]) {
let temp = myArray[0];
myArray[0] = myArray[2];
myArray[2] = temp;
}
// 最后还要比较一下 (自己想想为什么)
if (myArray[0] > myArray[1]) {
let temp = myArray[0];
myArray[0] = myArray[1];
myArray[1] = temp;
}
console.log(myArray);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
这里写成这样是为了演示分支,下章循环结构我们会有更简便的方法。实际上 JS 的数组还有一个自带的方法 .sort()
,可以从小到大排序数组里面的内容。
let myArray = [3, 1, 2];
myArray.sort();
console.log(myArray);
2
3
码风
貌似我们之前的 if 语句都是这样的:
if () {
//do something
} else if () {
//do something
} else {
//do something
}
2
3
4
5
6
7
其实 if 还可以写而成这样
if ()
{
//do something
}
else if ()
{
//do something
}
else
{
//do something
}
2
3
4
5
6
7
8
9
10
11
12
我们可以看到这里的开头大括号放在了下一行。其实其他的语言有很多也是这么写的。但是 JS 里面不推荐这么写,因为 JS 有个自动插入分号的机制,这本来是为了你万一忘记打分号也能正常运行程序用的,但是有时候会带来一点不愉快的事情:
return
{
statues : true
};
2
3
4
看起来你是想要返回一个对象,然而这种机制导致它实际上是这样的意思:
return; // BAD!!!
{
statues : true;
};
2
3
4
你可以看到它直接返回了,而后面那个对象就没有了。所以还是要这么写:
return {
statues : true
};
2
3
为了统一风格,我们就把所有的开头大括号不换行了。
for 循环
下面这个代码是一个简单的 for 循环。
for (let i = 0; i < 32; i++) { // 还记得自增运算符吧
console.log(i+1); // 想想为什么加一
}
2
3
我们可以这样看到循环的结构是这样的
for (初始化语句; 退出条件; 更新语句) {
一些需要执行的代码;
}
2
3
可以看到这里的基本结构
- 关键字 for,后跟一些括号。
- 在括号内,我们有三个项目,以分号分隔:
- 初始化语句 - 通常我们会在这里声明一个数字变量,这个变量可以配合后面的更新语句来统计循环当前执行了的次数。它也有时被称为计数变量。
- 退出条件 - 用来定义怎么退出循环。这个语句在每轮循环之前都会判断,如果满足条件就进入循环,如果不满足这个条件就退出循环。
- 更新语句 - 在每轮循环结束的时候执行。它通常用于增加(或在某些情况下递减)计数器变量,使其更接近退出条件值。
- 包含一些代码的花括号 - 每次循环时都会运行花括号里面的代码。
那么我们看下下面的代码:
let cats = ['Bill', 'Jeff', 'Pete', 'Biggles', 'Jasmin'];
let info = 'My cats are called ';
for (let i = 0; i < cats.length; i++) {
info += cats[i] + ', ';
}
console.log(info);
2
3
4
5
6
7
8
这里每次都把 cats 里面的元素加到 info 的末尾,输出就是这样:
My cats are called Bill, Jeff, Pete, Biggles, Jasmin,
- 迭代器 i 从 0 开始(
let i = 0
)。 - 循环将会一直运行直到 i 不再小于 cats 数组的长度 (
cats.length
)。 - 在循环中,我们将当前的
cats[i]
以及逗号和空格拼接 (+=
) 到 info 变量的末尾。 所以:- 在第一次运行中,i 为 0,所以
cats[0] +','
将被拼接到 info 上。 - 在第二次运行中,i 为 1,所以
cats[1] +','
将被拼接到 info 上。 - 每次循环运行后,i 都会加上 1,然后进程将再次启动,直到退出条件。
- 在第一次运行中,i 为 0,所以
- 当等于
cats.length
时,循环将停止,浏览器将移动到循环下面的下一个代码位。
使用 break 退出循环
在循环当中,我们有时候需要在中途退出。还是上面的例子,如果我们需要找到 cats
数组里面的 Jeff 这个名字所在的下标,找到就退出,那可能就要写成这样
let cats = ['Bill', 'Jeff', 'Pete', 'Biggles', 'Jasmin'];
let cur;
for (let i = 0; i < cats.length; i++) {
if ('Jeff' === cats[i] ) {
cur = i;
break;
}
}
console.log(cur);
2
3
4
5
6
7
8
9
10
11
输出就是
i = 0
i = 1
1
2
3
其中循环执行到 i = 1
的时候,循环就因为满足了 if 的条件,然后被 break 语句退出了,没有执行到 i < cats.length
这个条件。
使用 continue 跳过迭代
continue 语句以类似的方式工作,而不是完全跳出循环,而是跳过当前循环的下面部分,直接进入执行下一个循环。
比如说我们需要在遇到名字 Jeff
的时候不输出,就可以这么实现:
for (let i = 0; i < cats.length; i++) {
if ('Jeff' === cats[i] ) {
continue;
}
console.log(cats[i]);
}
2
3
4
5
6
输出
Bill
Pete
Biggles
Jasmin
2
3
4
这里在名称是 Jeff
的时候, continue
语句被执行,导致了后面的语句跳过去了,所以只有 Jeff
没有输出。
while 和 do while 循环
除了 for 循环之外, JS 还有两种循环:while 和 do while 循环。while 循环只有在入口处包含一个条件判断语句,如果满足就会进入循环。
比如之前的那个打印 cats 数组内容的程序,使用 while 循环我们就可以写成这个样子:
let cats = ['Bill', 'Jeff', 'Pete', 'Biggles', 'Jasmin'];
let info = 'My cats are called ';
let i = 0;// 初始化语句被放在外面了,成了一个普通的声明变量
while (i < cats.length) { // 这里只有一个条件判断
info += cats[i] + ', ';
i++; // 更新变成了普通代码
}
console.log(info);
2
3
4
5
6
7
8
9
10
还有一个 do while 循环和它类似,不过条件判断是放在结尾的,也就是循环节结束之后判断是否继续循环。
let i = 0;
do {
info += cats[i] + ', ';
i++;
} while (i < cats.length);
2
3
4
5
函数的引入
现有有一个大于等于 2 的正整数 a ,现在我们需要判断这个数字是不是质数。质数是只有 1 和它自己两个因数的数。对于所有的大于 2 的数,我们需要枚举那些大于 2 小于等于根号 a 的所有数字,一一确定它们是不是 a 的因数,如果都找不到那么它肯定是质数。
let a = 114514; // 很明显这是个合数
let isPrime = false; // 一个 boolen ,记录是否为质数
if (a === 2) {
isPrime = true;
} else {
isPrime = true; // 先暂时赋值,如果后面没被覆盖肯定是质数
for (let i = 2; i <= Math.sqrt(a); i++) {
if (a % i === 0) {
isPrime = false;
}
}
}
if (isPrime) {
console.log("是质数。");
} else {
console.log("不是质数。");
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
看起来很合理,但是如果有三个数,分别是 a, b, c。或者有三十个数,你是不是要把上面那个 if else 和循环都复制一下?这么做的话太麻烦了。所以我们有更好的方法:函数 (function) 。这就是上面写成函数的例子:
function judgePrime(a) {
let isPrime = false;
if (a === 2) {
isPrime = true;
} else {
isPrime = true; // 先暂时赋值,如果后面没被覆盖肯定是质数
for (let i = 2; i <= Math.sqrt(a); i++) {
if (a % i === 0) {
isPrime = false;
}
}
}
return isPrime;
}
let a = 114514, b = 17;
if (judgePrime(a)) {
console.log(a + " 是质数。");
} else {
console.log(a + " 不是质数。");
}
if (judgePrime(b)) {
console.log(b + " 是质数。");
} else {
console.log(b + " 不是质数。");
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
我们可以看出函数的结构:
function 函数名(参数) {
// 主体代码块
return;
}
2
3
4
一个函数定义(也称为函数声明,或函数语句)由一系列的 function 关键字组成,依次为:
- function 关键字。
- 函数的名称。
- 函数参数列表,包围在括号中并由逗号分隔。
- 定义函数的 JavaScript 语句,用大括号 {} 括起来。
比如我们从定义一个简单的乘方运算函数开始,这个函数可以得到给定数字的平方:
function square(number) {
return number * number;
};
2
3
函数 square 使用了一个参数,叫作 number 。这个函数只有一个语句,它说明该函数将函数的参数 number 自乘(就是平方)后返回。函数的 return 语句确定了函数的返回值。返回值就像普通的数值一样。比如下面这个代码:
function square(number) {
return number * number;
}
let x = square(3);
2
3
4
它执行了这样的操作:
- 声明一个 square 函数。
- 调用函数,将 3 作为参数传给函数,此时函数的 number 变量的值变成了 3 。
- 程序继续向下运行,计算 number * number 的值。
- 将计算出来的值返回回来。
- 然后
let x = square(3)
就相当于let x = 9
,x 就变成了 9 了。
可以非常清楚的非常清楚地看到,调用函数的方式是在函数名后加上一对括号,括号中包含参数。如果有多个参数就可以用逗号分隔:
function add(a, b) {
return a + b;
}
let x = add(1, 3); // x = 1 + 3
2
3
4
有些函数可以没有返回值,比如我有一个输出的函数:
function print(x) {
console.log("你的参数是 " + x);
}
print(12);
print('aaa');
2
3
4
5
这种情况下面,函数将会返回默认值 undefined
。
函数的特性
赋值
好像有点跑题,不过这个也是有关联的。
我们都知道那些基本数据是在赋值的时候,是可以被复制的:
> let val1 = 23;
undefined
> let val2 = val1; // val2 是 val1 的一个拷贝
undefined
> val1 = 11; // 修改 val1
11
> val2; // val2 没变
23
> let str1 = "abc"; // 下面同理
undefined
> let str2 = str1;
undefined
> str2 = "aaa";
'aaa'
> str1;
'abc'
>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
但是,不同于之前的那些基本数据,比如字符串和数字,数组和对象在使用 =
进行赋值的时候,其实没有进行复制,而是让两者指向同一个数组或者对象,就像是同一个数组或者对象有了两个名字,例如:
> let arr1 = [12, 22];
undefined
> let arr2 = arr1; // 两个其实是同一个
undefined
> arr1;
[ 12, 22 ]
> arr2;
[ 12, 22 ]
> arr2[1] = 1; // 直接修改,结果两个变量都变了,说明是指向同一个数组的
1
> arr1;
[ 12, 1 ]
> arr2;
[ 12, 1 ]
2
3
4
5
6
7
8
9
10
11
12
13
14
类似的,对象也是一样。如果要复制数组的话,可以使用 Array.from()
方法。当然我们可以使用更简单的 ...
语法来进行展开,在新数组中复制。对象可以参考这个语法。
> let arr1 = [12, 22];
undefined
> let arr2 = Array.from(arr1);
undefined
> let arr3 = [...arr1] // 这样也是等价的
undefined
> arr1;
[ 12, 22 ]
> arr2;
[ 12, 22 ]
> arr3;
[ 12, 22 ]
> arr2[1] = 1;
1
> arr1; // 现在这个值没有变了
[ 12, 22 ]
> arr2;
[ 12, 1 ]
> let obj1 = { a: 1; b: 2};
undefined
> let obj2 = { ...obj1 }; // 对象也可以这么复制
undefined
> obj2;
{ a: 1, b: 2 }
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
参数
函数的参数传入之后,如果我们修改了参数,会发生什么?
直接跑一遍代码,清楚明了:
function test(x) {
console.log("In function test, x = " + x);
x = 3;
console.log("After you changed, x = " + x);
}
let a = 1;
console.log("Before called the function, a = " + a);
test(a);
console.log("After called the function, a = " + a);
2
3
4
5
6
7
8
9
10
输出:
Before called the function, a = 1
In function test, x = 1
After you changed, x = 3
After called the function, a = 1
2
3
4
是不是非常清楚,函数 test
改了一个寂寞,最后 a 的值还是没变,其实函数的参数传递,就类似对每个参数进行了一次赋值,函数里面的变量和外面的不是同一个变量。类似的,如果传入的是数组和对象,情况就不太一样了:
function test(x) {
console.log("In function test, x = " + x.join(','));
x[0] = 3;
console.log("After you changed, x = " + x.join(','));
}
let a = [1, 3, 5];
console.log("Before called the function, a = " + a.join(','));
test(a);
console.log("After called the function, a = " + a.join(','));
2
3
4
5
6
7
8
9
10
输出:
Before called the function, a = 1,3,5
In function test, x = 1,3,5
After you changed, x = 3,3,5
After called the function, a = 3,3,5
2
3
4
定义域
函数里面的定义的值不会占用掉外面的名称,更不会占用掉其他函数里面定义的变量名。比如说这个:
let x = "x global";
function test1() {
let x = "x in test1()";
console.log(x);
}
function test2() {
let x = "x in test2()";
console.log(x);
}
test1();
test2();
console.log(x);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
输出:
x in test1()
x in test2()
x global
2
3
函数和对象
我们之前谈过了对象,现在是进一步扩充它的时候了。
函数作为值
函数可以被作为一个值赋值给一个变量:
function add(a, b) {
return a + b;
}
let myadd = add;
let x = myadd(1, 3);
2
3
4
5
更加常用的是这么写:
let myadd = (a, b) => {
return a + b;
}
2
3
这种情况下面会创造一个箭头函数,并且将这个函数赋值给 myadd 变量,使它的值成为一个函数,并且可以使用函数的方式调用。
箭头函数的特点,就是会把 this 绑定到当前的对象,而不是一个新对象。这个我们下一节会提到。因为 JS 的 this 和其他一系列灾难性的设计,我们不得不匀出一节内容单独讲这些方式。
方法
那么,既然函数可以赋值给普通变量,那么也可以赋值给一个对象成员了。一个函数成员叫做方法。,比如我们举个例子:
let cat = {
name: "Ket",
say() {
console.log("My name is " + this.name);
}
}
cat.say();
2
3
4
5
6
7
this
指的是调用这个函数的对象。其实这里隐藏了很多的问题,我们下节会专门指出。这里我们设计的方案可以确保它正常使用。
大家都是函数
那么,我们可以了解了,之前 console.log()
什么 join()
什么 split()
什么 Math.floor()
,通通都是函数或者方法,就和我们写出来的一样。
下一步
到了现在 JS 的基本语法你都已经掌握了,下面到了要拿这个新的工具去做点什么的时候了。
下一章: DOM!我们将会正式用 JS 去对网页做点事情。
练习
还记得 Tux 么?之前它让你写了一个计算 BMI 的程序,现在它要你去把这个程序改一下,判断 Tux 的 BMI 是不是正常的。
下面是程序的开头,你要用给出的体重 (变量 weight
) 和身高 (变量 height
) 来计算 BMI,同时判断 BMI 水平,输出 “偏瘦”(小于 18.5) “正常” (在 18.5-23.9 之间) 和 “偏胖” (大于 23.9)。(BMI = 体重 / 身高 ^2)
let weight = 82;
let height = 1.61;
2
把一个斐波那契数组算到第 45 个,这里我们给出了前两个,你需要自己计算每个数,并且存到数组里面,最后输出整个数组。(下标从 0 开始!)
let fibonacci = [1, 1];