Skip to content
This page has been auto-translated and may contain errors.View in English

运算符和比较 ​

docs.scrimba.com

假设你在跟踪购物车。你把价格加起来,应用折扣,检查总额是否达到免运费阈值,并决定购物车为空时是否显示警告。这些步骤中的每一个都涉及运算符:一个符号,接受一个或两个值并产生一个新值。本章涵盖你经常使用的运算符,从对数字进行算术运算到比较值和组合真假测试。

算术和赋值 ​

**算术运算符**执行你已经知道的数学运算:+ 加法,- 减法,* 乘法,/ 除法。还有一个你可能没见过:%,余数运算符,它给出除法后的余数。

js
const total = 8 + 5; // 13
const change = 20 - 13; // 7
const leftover = 17 % 5; // 2,因为 5 整除进 17 三次,剩余 2
console.log(total); // 13

当你想基于一个值本身来更新它时,有一个简写方法。不必写 score = score + 10,你可以写 score += 10。意思相同:取当前值,加 10,存储结果回去。

js
let score = 100;
score += 10; // score 现在是 110
score -= 30; // score 现在是 80
console.log(score); // 80

要将一个数字上升或下降恰好一个,++ 和 -- 一步完成。count++ 加一,count-- 减一。

js
let lives = 3;
lives--; // lives 现在是 2
console.log(lives); // 2

**算术运算符**是 +、-、*、/ 和 %。前四个很熟悉;% 值得再看一下。它返回除法的余数,这使它成为"这能整除吗?"检查的标准工具,也用于将一个值包裹回范围内。

js
const isEven = 10 % 2; // 0,所以 10 是偶数
const isOdd = 7 % 2; // 1,所以 7 是奇数
console.log(isEven); // 0

从自身更新一个值很常见,有简写形式。每个算术运算符都与 = 配对形成复合赋值:+=、-=、*=、/=、%=。所以 price *= 1.2 读作"将 price 乘以 1.2 并存储回去",省去了写 price = price * 1.2 的麻烦。

js
let price = 40;
price *= 1.2; // 增加 20 百分比,price 现在是 48
price -= 8; // price 现在是 40
console.log(price); // 40

要恰好改变一个值,++ 和 -- 就地递增和递减。当你在步进计数器时它们读起来很清楚,你会经常看到它们。

js
let attempts = 0;
attempts++; // attempts 现在是 1
console.log(attempts); // 1

**算术运算符**在四个基本运算符上没有太多惊喜,所以真正值得关注的两个是 % 和 + 的重载。余数运算符 % 返回整数式除法后的余数,它保留左操作数的符号:-7 % 3 是 -1,不是 2,这会让期望数学模的人出错。它是整除性测试和将索引包裹回范围的标准方法。

js
const remainder = 17 % 5; // 2
const negative = -7 % 3; // -1,符号跟随左操作数
console.log(negative); // -1

+ 运算符是重载的:对两个数字它加法,但如果任一侧是字符串,它连接,强制另一侧为字符串。那一条规则是一整类惊讶的根源,所以在加法前命名你拥有的东西。

js
const sum = 5 + 3; // 8
const glued = "5" + 3; // "53",3 变成了字符串
console.log(glued); // 53

复合赋值(+=、-=、*=、/=、%=)是从自身更新绑定的习用方法,它继承相同的 + 重载:msg += count 将 count 字符串化。递增和递减运算符 ++ 和 -- 有前缀和后缀两种形式,在表达式返回的值上有所不同:x++ 计算为旧值然后递增,++x 递增然后计算为新值。在表达式中这个区别很重要,所以在其值不重要的单独语句上保持它们。对任何你用这种方式重新赋值的东西使用 let,因为 const 绑定不能被更新。

js
let counter = 5;
const before = counter++; // before 是 5,counter 现在是 6
const after = ++counter; // after 是 7,counter 现在是 7
console.log(`${before} ${after}`); // 5 7
Juno算术和赋值 数学运算符是你期望的那些,加上 % 用于除法后的余数。当你从自身更新一个值时,使用简写:score += 10 而不是 score = score + 10。++ 或 -- 将一个数字上升或下降一个。我花了一段时间才信任这些,但它们是同样的数学,只是写得更短。
Juno算术和赋值 五个算术运算符,% 用于余数在整除性检查中赢得了它的地位。复合赋值如 price *= 1.2 一步更新一个值自身,++ 或 -- 步进一个。对任何你计划用这种方式更新的东西使用 let,因为 const 不能被重新赋值。
Juno算术和赋值 两件事保持敏锐:% 保留左操作数的符号,所以 -7 % 3 是 -1,+ 在任一侧是字符串时连接。前缀对后缀 ++ 返回不同的值,所以在其不能出现问题的单独语句上保持它们。其余算术运算符的行为完全如你所料。

比较 ​

一个**比较**询问关于两个值的问题,并用 true 或 false 回答。那些真假答案是布尔值,这是你的程序稍后用来做决定的东西。

你最常用的比较是 ===,它询问"这两个完全相等吗?"

js
const answer = 42;
console.log(answer === 42); // true
console.log(answer === 7); // false

它的反面是 !==,它询问"这两个不同吗?"对于顺序有 <、>、<= 和 >=,它们像数学中那样工作:小于、大于、小于等于、大于等于。

js
const age = 18;
console.log(age >= 18); // true
console.log(age !== 21); // true

总是用 === 比较,从不用 ==。双等号版本有一个让人惊讶的怪癖,三等号版本每次都做你期望的。

一个**比较**取两个值并返回一个布尔值,true 或 false。那个布尔值是你将在条件语句中写的决定的原始材料,所以正确进行比较很重要。

使用 === 表示相等,!== 表示不相等。对于顺序有 <、>、<= 和 >=。

js
const stock = 3;
console.log(stock === 0); // false
console.log(stock > 0); // true
console.log(stock <= 5); // true

选择 === 而不是 == 的原因是它们不是同一个运算符。===(严格相等)比较值和类型,只有在两者都匹配时才返回 true。==(宽松相等)首先将两侧转换为公共类型,然后比较,这产生的结果几乎没人能预测。

js
console.log(0 == ""); // true,很少是你想要的
console.log("1" == 1); // true,字符串变成了一个数字
console.log("1" === 1); // false,不同的类型

每次都默认使用 === 和 !==。它们不进行转换就比较,所以你永远不必推理转换规则。留下 == 用于阅读旧代码,不要用于写新代码。

一个**比较**返回一个布尔值,运算符分为两个家族:相等(===、!==)和关系(<、>、<=、>=)。关系运算符在数字上很直接。相等运算符是真正的决定所在,因为 JavaScript 给你两个。

===(严格相等)只有当两个操作数共享一个类型和值时才返回 true,没有转换。==(宽松相等)运行一个抽象相等算法,首先将操作数强制转换为公共类型,实际规则值得知道,这样你可以阅读旧代码:比较一个数字和一个字符串将字符串转换为数字("1" == 1 是 true);比较一个布尔值到任何东西将布尔值首先转换为数字(true == 1、false == 0);null == undefined 是 true 但两者都不等于其他任何东西;NaN 等于没有任何东西,甚至不等于它自己。

js
console.log(0 == ""); // true,"" 变成 0
console.log("1" == 1); // true,"1" 变成 1
console.log(null == undefined); // true,一个特殊情况
console.log(NaN === NaN); // false,NaN 永远不等于任何东西

使用 === 和 !==;将 == 视为只读知识。要点不是记住每个强制转换路径,而是绕过它们:严格相等没有强制转换,所以其结果直接从操作数读出。== 值得一提的唯一地方是 null == undefined 检查,某些代码库故意使用它来一次捕捉两个 null 值,但本章后面的空值运算符更清楚地完成了这项工作。

Juno比较 比较询问关于两个值的是或否问题,并用 true 或 false 回答。使用 === 检查相等,!== 表示"不同",<、>、<=、>= 表示顺序。坚持使用 === 并跳过 ==,因为双等号版本有一个惊讶,三等号版本表现良好。
Juno比较 每个比较都给你一个布尔值,这是你接下来写的决定的燃料。=== 一起检查值和类型;== 首先转换两侧,产生像 0 == "" 是 true 这样的怪异。默认使用 === 和 !==,这样你永远不必推理转换,留下 == 用于阅读旧代码。
Juno比较 严格相等在没有强制转换的情况下比较类型和值,所以其答案直接从操作数读出。宽松 == 在幕后运行一个转换算法,这就是为什么 "1" == 1 是 true 而 NaN 等于没有任何东西。写 === 和 !== 并保留强制转换规则作为旧代码的只读知识。

逻辑运算符和真值 ​

通常你需要组合多个比较。**逻辑运算符**让你这样做:&&(与),||(或),和 !(非)。

&& 只有在两侧都是 true 时才是 true。|| 在至少一侧是 true 时是 true。! 将一个布尔值翻转为其相反的值。

js
const age = 20;
const hasTicket = true;
console.log(age >= 18 && hasTicket); // true,两个都是 true
console.log(age < 13 || hasTicket); // true,一侧足够
console.log(!hasTicket); // false,翻转

有一个相关的想法叫做真值和假值。在真或假的设置中,JavaScript 将某些非布尔值视为好像它们是 false。假值是 false、0、""(一个空字符串)、null、undefined 和 NaN。其他所有东西都算作真值。

js
const name = "";
console.log(!name); // true,一个空字符串是假值

**逻辑运算符**组合布尔值:&&(与)只有当两个操作数都是 true 时才是 true,||(或)当至少一个是时是 true,!(非)反转一个布尔值。

js
const loggedIn = true;
const isAdmin = false;
console.log(loggedIn && isAdmin); // false,两个都必须是 true
console.log(loggedIn || isAdmin); // true,一个足够
console.log(!isAdmin); // true

运算符不止于真实布尔值,因为 JavaScript 将每个值排序成真值或假值。假值集很小,值得记住:false、0、""、null、undefined 和 NaN。其他一切都是真值,包括 "0"、"false" 和任何非空字符串。这让你可以写一个检查如 !username 来意思是"没有用户名被输入"。

js
const username = "";
console.log(!username); // true,"" 是假值
console.log(Boolean("hello")); // true,一个非空字符串是真值

逻辑运算符是 &&、|| 和 !,关键的洞察是 && 和 || 不返回一个布尔值。它们短路:每个计算左操作数,只有在必须时才计算右操作数,然后返回其中一个实际操作数,不是一个强制的 true 或 false。

a || b 如果 a 是真值则返回 a,否则返回 b。a && b 如果 a 是假值则返回 a,否则返回 b。那个行为基于假值集:false、0、""、null、undefined 和 NaN 是六个假值,其他一切都是真值。

js
const name = "";
const display = name || "Guest"; // "Guest",因为 "" 是假值
const label = "Wei" && "active"; // "active",左是真值所以它返回右
console.log(display); // Guest

|| 默认值技巧有一个尖锐的边缘,这就是为什么 ??(空值合并)存在。|| 在左侧是任何假值时回退,所以 0 || 10 是 10 和 "" || "n/a" 是 "n/a",甚至当 0 和 "" 是你想保留的有效输入时。?? 只有当左侧是 null 或 undefined 时才回退,将 0 和 "" 视为实际值。

js
const count = 0;
console.log(count || 5); // 5,但 0 是一个实际值
console.log(count ?? 5); // 0,?? 只替换 null 或 undefined

当你需要基于一个条件在两个值之间选择时,一个完整的比较加上一个三元运算符(condition ? whenTrue : whenFalse)是单行形式,例如 age >= 18 ? "adult" : "minor"。那是一个预览;条件语句章节完整涵盖它和 if 语句。

Juno逻辑运算符和真值&& 是"两个",|| 是"任一个",! 翻转一个布尔值。顶部的是,JavaScript 将某些值视为假值:false、0、""、null、undefined 和 NaN 是假值,其他一切都是真值。这让 !name 代表"没有名字被给出"。
Juno逻辑运算符和真值&& 需要两侧都是真,|| 需要一个,! 反转。每个值也要么是真值,要么是假值,假值集足够小可以记住:false、0、""、null、undefined、NaN。注意 "0" 和 "false" 是字符串,所以两个都是真值。
Juno逻辑运算符和真值&& 和 || 短路并返回一个操作数,不是一个布尔值,这就是为什么 name || "Guest" 作为默认工作。那个默认值有一个边缘:|| 替换每个假值,所以当 0 和 "" 是你想保留的输入时使用 ??。六个假值是全部情况,所以学习那个集合,其余的就会落出来。

运算符从这里何去何从 ​

运算符是语言的动词:它们取你存储的值,并将它们转变为结果和决定。算术产生新数字,比较产生布尔值,逻辑运算符组合那些布尔值成为你的程序作用的是或否答案。

那些布尔值自己做不了太多。下一步是使用它们来掌舵你的代码,当一个检查通过时运行一个分支,当它失败时运行另一个,这就是条件语句章节的内容。如果这里的任何值感觉模糊,数据类型章节是数字、字符串和布尔值得到全部处理的地方。