Skip to content

一. 运算符和运算元


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 + 35
    +连接字符串'hello ' + 'world''hello world'
    -减法2 - 3-1
    *乘法2 * 36
    /除法5 / 22.5
    %取余数5 % 21
    **幂(ES72 ** 38
  • 取余运算符是 %,尽管它看起来很像百分数,但实际并无关联

    • a % b 的结果是 a 整除 b 的余数

      js
      10 % 3 // 1
  • 求幂运算 a**ba 提升至 ab 次幂(ES7 中的语法,也叫做 ES2016

    • 在数学中我们将其表示为 ab 次方

      js
      2**3 // 8

4. 赋值运算符

  • 前面我们使用的 = 其实也是一个运算符,被称之为赋值运算符

  • = 是一个运算符,而不是一个有着“魔法”作用的语言结构

    • 语句 x = value 将值 value 写入 x,然后返回 x
  • 链式赋值(Chaining assignments

    js
    var a, b, c;
    a = b = c = 2 + 2
    • 链式赋值从右到左进行计算
    • 首先,对最右边的表达式 2 + 2 求值,然后将其赋给左边的变量:c、b 和 a
    • 最后,所有的变量共享一个值
    • 但是从代码的可读性的角度来说,不推荐这种写法

    注意:

    • 点语法优先级高于赋值

      js
      var a = {n: 1}
      var b = a
      a.n = a = {n: 2}
        
      conole.log(a.n) // 2
      console.log(a) // {n: 2}
      image-20230410141946465

5. 原地修改

  • 什么是原地修改呢?

    • 我们经常需要对一个变量做运算,并将新的结果存储在同一个变量中

      js
      var a = 10, b = 20
      a = a + 10 // 20
      b = b * 2 // 40
    • 可以使用运算符+=*=来缩写这种表示

      js
      var a = 10, b = 20
      a += 10 // 20
      a *= 2 //40
  • 所有算术和位运算符都有简短的“修改并赋值”运算符/= -=

    运算符运算规则范例结果
    =赋值a = 55
    +=加后赋值a = 5, a += 27
    -=减后赋值a = 5, a -= 23
    *=乘后赋值a = 5, a *= 210
    /=除后赋值a = 5, a /= 22.5
    %=取模(余数)后赋值a = 5, a %= 21
    **=幂后赋值a = 5, a **= 225

6. 自增、自减

  • 对一个数进行加一、减一是最常见的数学运算符之一
  • 所以,对此有一些专门的运算符:
    • 自增++将变量加1
    • 自减--将变量减1
  • 自增/自减只能应用于变量
    • 将其应用于数值(比如 5++)则会报错
  • ++--的位置
    • 运算符++--可以置于变量前,也可以置于变量后
      • 当运算符置于变量后,被称为“后置形式”(postfix form):counter++
      • 当运算符置于变量前,被称为“前置形式”(prefix form):++counter
      • 两者都做同一件事:将变量counter1相加
    • 有什么区别吗?
      • 有,但只有当我们使用++/--的返回值时才能看到区别
      • 如果自增/自减的值不会被使用,那么两者形式没有区别
      • 如果我们想要对变量进行自增操作,并且 需要立刻使用自增后的值,那么我们需要使用前置形式
      • 前置形式返回一个新的值,但后置返回原来的值

7. 运算符的优先级

8. 比较运算符

  • 我们知道,在数学中有很多用于比较大小的运算符,在js中也有相似的比较:

    • 大于 / 小于:a > b,a < b
    • 大于等于 / 小于等于:a >= b,a <= b
    • 检查两个值的相等:a == b,请注意双等号==表示相等性检查,而单等号a = b表示赋值
    • 检查两个值不相等:不相等在数学中的符号是 ,但在js中写成 a != b
  • 比较运算符的结果都是Boolean类型的

    运算符运算规则范例结果
    ==相等4 == 3false
    !>不等于4 != 3true
    >小于4 < 3true
    <大于4 > 3false
    >=小于等于4 <= 3false
    <=大于等于4 >= 3false

注意:

  • 双等号中,null undefined二者互等,跟除自身外其他任何值都是不等的
  • NaN 不等于任何值,包括其自身

9. == 和 === 区别

  • 普通的相等性检查==存在一个问题,它不能区分出0 false,或者空字符串和 false这类运算:
    • 这是因为在比较不同类型的值时,处于判断符号==两侧的值大部分会先被转化为Number类型
    • 空字符串和false也是如此,转化后它们都为数字 0
  • 如果我们需要区分0 false,该怎么办?
    • 严格相等运算符===在进行比较时不会做任何的类型转换
    • 换句话说,如果ab属于不同的数据类型,那么a === b不会做任何的类型转换而立刻返回 false
  • 同样的,“不相等”符号!=类似,“严格不相等”表示为 !==
  • 严格相等的运算符虽然写起来稍微长一些,但是它能够很清楚地显示代码意图,降低你犯错的可能性

二. 分支语句


1. 程序的执行顺序

  • 在程序开发中,程序有三种不同的执行方式:

    • 顺序 —— 从上向下,顺序执行代码

    • 分支 —— 根据条件判断,决定执行代码的 分支

    • 循环 —— 让 特定代码 重复 执行

      image-20220510233648880

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,就会执行对应的代码块
    image-20220511231355732
  • 案例一: 如果小明考试超过90分, 就去游乐场

    • “如果”相当于js中的关键字if

    • 分数超过90分是一个条件(可以使用>符号)

      js
      if (grade > 90) {
        alert('去游乐场')
      }
  • 案例二:单位5元/斤的苹果,如果购买超过5斤,那么立减8元

    • 注意:这里我们让用户输入购买的重量,计算出最后的价格并且弹出结果

      js
      var nWeight = prompt('购买的重量')
      if (nWeight > 5) {
        alert('最终价格,减去8元:' + (5 * nWeight - 8))
      } else {
        alert('最终价格:' + 5 * nWeight)
      }

6. if语句的细节补充

  • 补充一:如果{}代码块中只有一行代码,那么{}可以省略
  • 补充二:if (…) 语句会计算圆括号内的表达式,并将计算结果转换为布尔型(Boolean
    • 转换规则和Boolean函数的规则一致
    • 0""nullundefined NaN都会被转换成 false
      • 因为它们被称为 " 假值(falsy)"
    • 其他值被转换为true,所以它们被称为 " 真值(truthy)"

7. 多分支语句:if.. else..

  • if 语句有时会包含一个可选的 "else" 块

  • 如果判断条件不成立,就会执行它内部的代码

    image-20220511232036253
  • 案例一:如果分数超过90分去游乐场,否则去上补习班

    • 满足条件时,做某些事情

    • 不满足(else),去做另外一些事情

      js
      if (grade > 90) {
        alert('去游乐场')
      } else {
        alert('去补习班')
      }
  • 案例二:m=20,n=30,比较两个数字的大小,获取较大的那个数字

    js
    var m = 20, n = 30
    if (m > n) {
      alert('m: ', m)
    } else {
      alert('n: ', n)
    }

8. 多分支结构:if.. else if.. else..

  • 有时我们需要判断多个条件

  • 我们可以通过使用else if子句实现

    image-20220511232258975
  • 案例: 分数评级

    • 考试分数大于90:优秀

    • 大于80小于等于90:良好

    • 大于60小于等于80:合格

    • 小于60分:不及格

      js
      if (grade > 90) {
        alert('优秀')
      } else if (grade > 80 && grade <= 90) {
        alert('良好')
      } else if (grade > 60 && grade <= 80) {
        alert('合格')
      } else {
        alert('不及格')
      }

三. 三元运算符


  • 有时我们需要根据一个条件去赋值一个变量

    • 比如比较数字大小的时候,获取较大的数字
    • 这个时候if else语句就会显得过于臃肿,有没有更加简介的方法呢?
  • 条件运算符:?

    • 这个运算符通过问号?表示
    • 有时它被称为三元运算符,被称为“三元”是因为该运算符中有三个操作数(运算元)
    • 实际上它是js中唯一一个有这么多操作数的运算符
  • 使用格式如下:

    js
    var result = condition ? value1 : value2
    • 计算条件结果,如果结果为真,则返回 value1,否则返回 value2
  • 案例一: m=20,n=30,比较两个数字的大小,获取较大的那个数字

    js
    var m = 20, n = 30
    console.log(m > n ? m : n)
  • 案例二:判断一个人是否是成年人了

    js
    console.log(age >= 18 ? '成年' : '未成年')

四. 认识逻辑运算符


  • 逻辑运算符,主要是由三个:

    • ||(或)&&(与)!(非)

    • 它可以将多个表达式或者值放到一起来获取到一个最终的结果

      运算符运算规则范例结果
      &&与:同时为真false && truefalse
      ``或:一个为真
      !非:取反!falsetrue
  • 有了逻辑运算符,我们就可以在判断语句中编写多个条件

1. || (逻辑或)

  • ||表示 " 或 " 运算符(也称为短路或):

    js
    var result = 10 || 20
    • 从左到右依次计算操作数
    • 处理每一个操作数时,都将其转化为布尔值(Boolean
    • 如果结果是true,就停止计算,返回这个操作数的初始值
    • 如果所有的操作数转换结果都是 false,则返回最后一个操作数

注意:

  • 返回的值是操作数的初始形式,不会转换为Boolean类型
  • 换句话说,一个或运算(||)的链,返回第一个真值,如果不存在真值,就返回该链的最后一个值

    js
    var name1 = ''
    var name2 = 0
    // 返回第一个真值,没有真值,默认返回最后一个值
    var nickname = name1 || name2 || 'anonymous' // 'anonymous'

2. && (逻辑与)

  • &&表示 " 与 " 运算符(也称为短路与):
js
var result = value1 && value2 && value3
// 返回第一个假值,没有假值,默认返回最后一个值
  • 从左到右依次计算操作数

  • 在处理每一个操作数时,都将其转化为布尔值(Boolean

  • 如果结果是 false,就停止计算,并返回这个操作数的初始值

  • 如果所有的操作数转换结果都是都是真值,则返回最后一个操作数

  • 换句话说,一个与运算(&&)的链,返回第一个假值,如果不存在假值,就返回该链的最后一个值

    js
    var 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
    • 步骤二:返回相反的值(取反
  • 两个非运算!!有时候用来将某个值转化为布尔类型:

    • 也就是,第一个非运算将该值转化为布尔类型并取反,第二个非运算再次取反

    • 最后我们就得到了一个任意值到布尔值的转化

      js
      var str = ''
      console.log('!!str: ', !!str) // !!str: false

六. switch语句

switch 是什么?

switch 是 JavaScript 中的一种分支控制语句,通过判断表达式的结果(或变量),在多个候选值(case)中选择执行路径。

核心判断规则

switch 表达式的结果(或变量),与每一个 case 后的值进行严格相等(===)比较,相等则执行相应的分支体的。

与 if / else 的本质区别

对比点switchif/else
判断方式值的严格相等判断(===)任意布尔表达式
是否支持范围判断❌ 不支持✅ 支持(> < >= && 等)
适用场景离散、枚举型取值区间判断、复杂条件

⚠️ 注意

switch 不适合做区间判断(如成绩、区间范围),更适合「状态码 / 枚举值 / 固定选项」类逻辑。

基本语法结构

js
switch (表达式|常量) {
  case 常量1:
    // 当 表达式 === 常量1 时执行
    break
  case 常量2:
    // 当 表达式 === 常量2 时执行
    break
  default:
    // 所有 case 都不匹配时执行
}

⚠️ 注意

至少要有 一个 case 语句

default 是可选的

case 后的值通常是:数字、字符串、布尔值、Symbol

case 穿透问题

什么是 case 穿透?

在 switch 中:一旦某个 case 匹配成功,如果没有遇到 break | return | throw,程序会继续向下执行后续所有 case + default 代码。

这种现象被称为:case 穿透(fall-through)。

case 穿透示例
js
const n = 2
switch (n) {
  case 1:
    console.log('case 1')
  case 2:
    console.log('case 2')
  case 3:
    console.log('case 3')
  default:
    console.log('default')
}

// output:
// case 2
// case 3
// default

⚠️ 注意

匹配行为只发生一次

命中 某个 case 后,后续不再做条件判断,而是顺序执行

break 的真实作用

立即终止当前 switch 语句并跳出。

阻止 case 穿透
js
const n = 2
switch (n) {
  case 1:
    console.log('case 1')
  case 2:
    console.log('case 2')
    break
  case 3:
    console.log('case 3')
  default:
    console.log('default')
}

// output:
// case 2

default 的正确理解

设计意图与常见用法:兜底分支

从语言设计目的与工程实践来看,default 的引入就是为了解决一个问题:

当没有任何 case 与 switch 表达式匹配时,应该执行哪一段逻辑?

因此,在绝大多数业务代码中,default 通常被当作兜底处理分支来使用:

示例
js
function getStatusText(status) {
  switch (status) {
    case 200:
      return '成功'
    case 404:
      return '未找到'
    default:
      return '未知状态'
  }
}

在工程实践和语义理解层面:

default 只在「所有 case 都不匹配」时执行

使用效果上 类似于 if / else 中的 else

执行机制层面:default 并不具备 else 的执行保证

需要进一步注意的是,上述“兜底效果”并不是 default 自身具备判断能力,而是由 switch 的执行模型 + 分支终止方式共同决定的。

从语言机制上讲,switch 的执行流程可以概括为:

  1. 先进行执行入口的定位:
    • 尝试匹配第一个严格相等的 case。
    • 若没有任何 case 命中,将执行入口定位到 default
  2. 从定位到的入口开始,执行后续所有代码,不再判断后续遇到 case 分支
  3. 执行过程中,遇到 break / return / throw 则跳出 switch,或直至整个 switch 执行结束。

default 本身:

❌ 不参与条件判断

❌ 不保证只在“不匹配 case”时执行

✅ 只是一个普通的分支入口标签

一个容易误解 default 本质的示例

同样受 case 穿透影响
js
switch (1) {
  case 1:
    console.log(1)
  default:
    console.log('default')
}
// output:
// 1
// default

当 case 1 匹配成功后,执行入口定位到该处

由于未显式终止分支(没有 break / return),程序会继续往后执行 default 分支。

如果 default 具备 else 的执行保证的话,这段代码中它就不应该执行。

正确且推荐的理解方式

在语言设计意图和常见用法上,default 是用于兜底处理的分支;

但在执行机制上,由于 switch 采用顺序执行模型,如果未显式终止分支,default 也会在 case 穿透时被执行, 因此 default 并不具备 else 那样的执行保证,它并不是判断条件,而只是一个普通的执行分支标签

不写 break 的场景

多个 case 共享同一段逻辑
js
switch (day) {
  case 1:
  case 2:
  case 3:
  case 4:
  case 5:
    return '工作日'
  case 6:
  case 0:
    return '周末'
}

这是刻意使用 case 穿透

工程中唯一被广泛接受的 fall-through 用法

switch 中的三种”终止方式“

关键字作用效果使用场景
break跳出 switch任意场景下的 switch 中
return跳出 switch,返回函数结果函数中的 switch
throw抛出一个错误并跳出当前函数任意场景下的 switch 中

如果三者都没有:一定会发生 case 穿透。

工程实践建议

✅ 结果型 switch(返回值) → 推荐使用 return

✅ 流程型 switch(执行操作) → 使用 break

❌ 禁止无意的 case 穿透

✅ 有意穿透时,务必添加注释

js
switch (n) {
  case 1:
  case 2:
    // intentional fall-through
    handleCommon()
    break
}

案例练习:播放模式

js
function getPlayModeText(mode) {
  switch (mode) {
    case 0:
      return '单曲循环'
    case 1:
      return '列表循环'
    case 2:
      return '随机播放'
    default:
      return '未知模式'
  }
}

如有转载或CV请标注本站原文地址