一. 运算符和运算元
1. 认识运算符
- 在小学的时候我们就学习了各种运算符,比如加号 +、乘号 *、减号 - 、除号/
- 几乎所有的编程语言都有各种各样的运算符(也被称之为操作符,
operators
)- 初次接触这些运算符,你会感觉种类繁多,难以记忆
- 但是并不需要特别担心,因为很多的运算符我们在以后的开发中,每天都会使用
- 多练习,不需要刻意去记忆
- 而且常见的高级语言运算符都是相似的,学了
js
运算符很容易掌握C/C++/OC/Python
等语言的运算符
- 计算机最基本的操作就是执行运算,执行运算时就需要使用运算符来操作:
- 比如
console.log(20 + 30)
中的+
号就是一个运算符 - 比如
console.log(20 * 30)
中的*
号也是一个运算符
- 比如
js
按照使用场景的不同将运算符分成了很多种类型:- 算术运算符、赋值运算符、 关系(比较)运算符、 逻辑运算符
2. 认识运算元
- 在正式开始运算之前,我们先学习一下常见的术语:
- 运算元 —— 运算符应用的对象
- 比如说乘法运算 5 * 2,有两个运算元
- 左运算元 5 和右运算元 2
- 有时候人们也称其为“参数”
- 如果一个运算符对应的只有一个运算元,那么它是 一元运算符
- 比如说一元负号运算符(
unary negation
)-
,它的作用是对数字进行正负转换
- 比如说一元负号运算符(
- 如果一个运算符拥有两个运算元,那么它是 二元运算符
- 比如 2 + 3
- 运算元 —— 运算符应用的对象
- 一元运算符通常我们是使用
–
和+
,-
号使用的会较多一些
3. 算术运算符
算术运算符用在数学表达式中,它的使用方式和数学中也是一样的
算术运算符是对数据进行计算的符号
运算符 运算规则 范例 结果 +
加法 2 + 3
5
+
连接字符串 'hello ' + 'world'
'hello world'
-
减法 2 - 3
-1
*
乘法 2 * 3
6
/
除法 5 / 2
2.5
%
取余数 5 % 2
1
**
幂( ES7
)2 ** 3
8
取余运算符是
%
,尽管它看起来很像百分数,但实际并无关联a % b
的结果是a
整除b
的余数js10 % 3 // 1
求幂运算
a**b
将a
提升至a
的b
次幂(ES7
中的语法,也叫做ES2016
)在数学中我们将其表示为
a
的b
次方js2**3 // 8
4. 赋值运算符
前面我们使用的
=
其实也是一个运算符,被称之为赋值运算符=
是一个运算符,而不是一个有着“魔法”作用的语言结构- 语句
x = value
将值value
写入x
,然后返回x
- 语句
链式赋值(
Chaining assignments
)jsvar a, b, c; a = b = c = 2 + 2
- 链式赋值从右到左进行计算
- 首先,对最右边的表达式 2 + 2 求值,然后将其赋给左边的变量:c、b 和 a
- 最后,所有的变量共享一个值
- 但是从代码的可读性的角度来说,不推荐这种写法
注意:
点语法优先级高于赋值
jsvar a = {n: 1} var b = a a.n = a = {n: 2} conole.log(a.n) // 2 console.log(a) // {n: 2}
5. 原地修改
什么是原地修改呢?
我们经常需要对一个变量做运算,并将新的结果存储在同一个变量中
jsvar a = 10, b = 20 a = a + 10 // 20 b = b * 2 // 40
可以使用运算符
+=
和*=
来缩写这种表示jsvar a = 10, b = 20 a += 10 // 20 a *= 2 //40
所有算术和位运算符都有简短的“修改并赋值”运算符:
/=
和-=
等运算符 运算规则 范例 结果 =
赋值 a = 5
5
+=
加后赋值 a = 5, a += 2
7
-=
减后赋值 a = 5, a -= 2
3
*=
乘后赋值 a = 5, a *= 2
10
/=
除后赋值 a = 5, a /= 2
2.5
%=
取模(余数)后赋值 a = 5, a %= 2
1
**=
幂后赋值 a = 5, a **= 2
25
6. 自增、自减
- 对一个数进行加一、减一是最常见的数学运算符之一
- 所以,对此有一些专门的运算符:
- 自增
++
将变量加1 - 自减
--
将变量减1
- 自增
- 自增/自减只能应用于变量
- 将其应用于数值(比如 5++)则会报错
++
和--
的位置- 运算符
++
和--
可以置于变量前,也可以置于变量后- 当运算符置于变量后,被称为“后置形式”(postfix form):
counter++
- 当运算符置于变量前,被称为“前置形式”(prefix form):
++counter
- 两者都做同一件事:将变量
counter
与1
相加
- 当运算符置于变量后,被称为“后置形式”(postfix form):
- 有什么区别吗?
- 有,但只有当我们使用
++/--
的返回值时才能看到区别 - 如果自增/自减的值不会被使用,那么两者形式没有区别
- 如果我们想要对变量进行自增操作,并且 需要立刻使用自增后的值,那么我们需要使用前置形式
- 前置形式返回一个新的值,但后置返回原来的值
- 有,但只有当我们使用
- 运算符
7. 运算符的优先级
- 运算符放到一起使用时会有一定的优先级
- 在
MDN
上给出了所有运算符的优先级
8. 比较运算符
我们知道,在数学中有很多用于比较大小的运算符,在
js
中也有相似的比较:- 大于 / 小于:a > b,a < b
- 大于等于 / 小于等于:a >= b,a <= b
- 检查两个值的相等:
a == b
,请注意双等号==
表示相等性检查,而单等号a = b
表示赋值 - 检查两个值不相等:不相等在数学中的符号是
≠
,但在js
中写成a != b
比较运算符的结果都是
Boolean
类型的运算符 运算规则 范例 结果 ==
相等 4 == 3
false
!>
不等于 4 != 3
true
>
小于 4 < 3
true
<
大于 4 > 3
false
>=
小于等于 4 <= 3
false
<=
大于等于 4 >= 3
false
注意:
- 双等号中,
null
和undefined
二者互等,跟除自身外其他任何值都是不等的NaN
不等于任何值,包括其自身
9. == 和 === 区别
- 普通的相等性检查
==
存在一个问题,它不能区分出0
和false
,或者空字符串和false
这类运算:- 这是因为在比较不同类型的值时,处于判断符号
==
两侧的值大部分会先被转化为Number
类型 - 空字符串和
false
也是如此,转化后它们都为数字0
- 这是因为在比较不同类型的值时,处于判断符号
- 如果我们需要区分
0
和false
,该怎么办?- 严格相等运算符
===
在进行比较时不会做任何的类型转换 - 换句话说,如果
a
和b
属于不同的数据类型,那么a === b
不会做任何的类型转换而立刻返回false
- 严格相等运算符
- 同样的,“不相等”符号
!=
类似,“严格不相等”表示为!==
- 严格相等的运算符虽然写起来稍微长一些,但是它能够很清楚地显示代码意图,降低你犯错的可能性
二. 分支语句
1. 程序的执行顺序
在程序开发中,程序有三种不同的执行方式:
顺序 —— 从上向下,顺序执行代码
分支 —— 根据条件判断,决定执行代码的 分支
循环 —— 让 特定代码 重复 执行
2. 代码块的理解
代码块是多行执行代码的集合,通过一个**花括号
{}
**放到了一起在开发中,一行代码很难完成某一个特定的功能,我们就会将这些代码放到一个代码块中
js{ var msg = 'hello world' console.log(msg) }
在
js
中,我们可以通过流程控制语句来决定如何执行一个代码块:- 通常会通过一些关键字来告知
js
引擎代码要如何被执行 - 比如分支语句、循环语句对应的关键字等
- 通常会通过一些关键字来告知
3. 什么是分支结构?
- 程序是生活的一种抽象, 只是我们用代码表示了出来
- 在开发中, 我们经常需要根据一定的条件, 来决定代码的执行方向
- 如果 条件满足,才能做某件事情
- 如果 条件不满足,就做另外一件事情
- 分支结构
- 分支结构的代码就是让我们根据条件来决定代码的执行
- 分支结构的语句被称为判断结构或者选择结构
- 几乎所有的编程语言都有分支结构(C、C++、OC、JavaScript等等)
js
中常见的分支结构有:if
分支结构switch
分支结构
4. if分支语句
if
分支结构有三种:- 单分支结构
if...
- 多分支结构
if..else..
if..else if..else..
- 单分支结构
5. 单分支结构
单分支语句:
if
if(...)
语句计算括号里的条件表达式,如果计算结果是true
,就会执行对应的代码块
案例一: 如果小明考试超过90分, 就去游乐场
“如果”相当于
js
中的关键字if
分数超过90分是一个条件(可以使用
>
符号)jsif (grade > 90) { alert('去游乐场') }
案例二:单位5元/斤的苹果,如果购买超过5斤,那么立减8元
注意:这里我们让用户输入购买的重量,计算出最后的价格并且弹出结果
jsvar nWeight = prompt('购买的重量') if (nWeight > 5) { alert('最终价格,减去8元:' + (5 * nWeight - 8)) } else { alert('最终价格:' + 5 * nWeight) }
6. if语句的细节补充
- 补充一:如果
{}
代码块中只有一行代码,那么{}
可以省略 - 补充二:
if (…)
语句会计算圆括号内的表达式,并将计算结果转换为布尔型(Boolean
)- 转换规则和
Boolean
函数的规则一致 0
、""
、null
、undefined
和NaN
都会被转换成false
- 因为它们被称为 " 假值(
falsy
)"
- 因为它们被称为 " 假值(
- 其他值被转换为
true
,所以它们被称为 " 真值(truthy
)"
- 转换规则和
7. 多分支语句:if.. else..
if
语句有时会包含一个可选的 "else
" 块如果判断条件不成立,就会执行它内部的代码
案例一:如果分数超过90分去游乐场,否则去上补习班
满足条件时,做某些事情
不满足(else),去做另外一些事情
jsif (grade > 90) { alert('去游乐场') } else { alert('去补习班') }
案例二:m=20,n=30,比较两个数字的大小,获取较大的那个数字
jsvar m = 20, n = 30 if (m > n) { alert('m: ', m) } else { alert('n: ', n) }
8. 多分支结构:if.. else if.. else..
有时我们需要判断多个条件
我们可以通过使用
else if
子句实现案例: 分数评级
考试分数大于90:优秀
大于80小于等于90:良好
大于60小于等于80:合格
小于60分:不及格
jsif (grade > 90) { alert('优秀') } else if (grade > 80 && grade <= 90) { alert('良好') } else if (grade > 60 && grade <= 80) { alert('合格') } else { alert('不及格') }
三. 三元运算符
有时我们需要根据一个条件去赋值一个变量
- 比如比较数字大小的时候,获取较大的数字
- 这个时候
if else
语句就会显得过于臃肿,有没有更加简介的方法呢?
条件运算符:
?
- 这个运算符通过问号
?
表示 - 有时它被称为三元运算符,被称为“三元”是因为该运算符中有三个操作数(运算元)
- 实际上它是
js
中唯一一个有这么多操作数的运算符
- 这个运算符通过问号
使用格式如下:
jsvar result = condition ? value1 : value2
- 计算条件结果,如果结果为真,则返回
value1
,否则返回value2
- 计算条件结果,如果结果为真,则返回
案例一: m=20,n=30,比较两个数字的大小,获取较大的那个数字
jsvar m = 20, n = 30 console.log(m > n ? m : n)
案例二:判断一个人是否是成年人了
jsconsole.log(age >= 18 ? '成年' : '未成年')
四. 认识逻辑运算符
逻辑运算符,主要是由三个:
||
(或)、&&
(与)、!
(非)它可以将多个表达式或者值放到一起来获取到一个最终的结果
运算符 运算规则 范例 结果 &&
与:同时为真 false && true
false
` ` 或:一个为真 !
非:取反 !false
true
有了逻辑运算符,我们就可以在判断语句中编写多个条件
1. || (逻辑或)
||
表示 " 或 " 运算符(也称为短路或):jsvar result = 10 || 20
- 从左到右依次计算操作数
- 处理每一个操作数时,都将其转化为布尔值(
Boolean
) - 如果结果是
true
,就停止计算,返回这个操作数的初始值 - 如果所有的操作数转换结果都是
false
,则返回最后一个操作数
注意:
- 返回的值是操作数的初始形式,不会转换为
Boolean
类型
换句话说,一个或运算(
||
)的链,返回第一个真值,如果不存在真值,就返回该链的最后一个值jsvar name1 = '' var name2 = 0 // 返回第一个真值,没有真值,默认返回最后一个值 var nickname = name1 || name2 || 'anonymous' // 'anonymous'
2. && (逻辑与)
&&
表示 " 与 " 运算符(也称为短路与):
var result = value1 && value2 && value3
// 返回第一个假值,没有假值,默认返回最后一个值
从左到右依次计算操作数
在处理每一个操作数时,都将其转化为布尔值(
Boolean
)如果结果是
false
,就停止计算,并返回这个操作数的初始值如果所有的操作数转换结果都是都是真值,则返回最后一个操作数
换句话说,一个与运算(
&&
)的链,返回第一个假值,如果不存在假值,就返回该链的最后一个值jsvar obj; var obj2 = 1; obj.name // 这样使用,有风险,obj为假值不存在name属性,会报错:Cannot read properties of undefined (reading 'name') // 这样使用,代码更严谨,会先判断obj是否为真值,为真值则执行后续运算元,为假值,则不会报错,会返回假值运算元的初始值 obj && obj.name // 这里obj为假值,其初始值为undefined,所以表达式返回obj的计算结果undefined obj2 && obj2.name // 这里obj2为真值,所以继续往后计算,obj2.name为假值,所以表达式为返回obj2.name的计算结果undefined
3. ! (逻辑非)
逻辑非运算符接受一个参数,并按如下运算:
- 步骤一:将操作数转化为布尔类型:
true / false
- 步骤二:返回相反的值(取反)
- 步骤一:将操作数转化为布尔类型:
两个非运算
!!
有时候用来将某个值转化为布尔类型:也就是,第一个非运算将该值转化为布尔类型并取反,第二个非运算再次取反
最后我们就得到了一个任意值到布尔值的转化
jsvar str = '' console.log('!!str: ', !!str) // !!str: false
六. switch语句
switch
是分支结构的一种语句:- 它是通过判断表达式的结果(或者变量)是否等于
case
语句的常量,来执行相应的分支体的
- 它是通过判断表达式的结果(或者变量)是否等于
与
if
语句不同的是,switch
语句只能做值的相等判断(使用全等运算符===
),而if
语句可以做值的范围判断switch
的语法:switch
语句有至少一个case
代码块和一个可选的default
代码块
jsswitch(表达式 or 常量) { case 常量1 : ... break case 常量2 : ... break default: ... }
补充:
case
穿透问题:- 一条
case
语句结束后,会自动执行下一个case
的语句 - 这种现象被称之为
case
穿透
- 一条
break
关键字- 通过在每个
case
的代码块后添加break
关键字来解决这个问题
- 通过在每个
注意事项:
- 这里的相等是严格相等
- 被比较的值必须是相同的类型才能进行匹配
- 案例练习:播放模式(单曲循环、循环播放、随机播放)