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 += countcount 字符串化。递增和递减运算符 ++-- 有前缀和后缀两种形式,在表达式返回的值上有所不同: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+ 在任一侧是字符串时连接。前缀对后缀 ++ 返回不同的值,所以在其不能出现问题的单独语句上保持它们。其余算术运算符的行为完全如你所料。

比较

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

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

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

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

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

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

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

逻辑运算符和真值

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

&& 只有在两侧都是 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。假值是 false0""(一个空字符串)、nullundefinedNaN。其他所有东西都算作真值。

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 将每个值排序成真值假值。假值集很小,值得记住:false0""nullundefinedNaN。其他一切都是真值,包括 "0""false" 和任何非空字符串。这让你可以写一个检查如 !username 来意思是"没有用户名被输入"。

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

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

a || b 如果 a 是真值则返回 a,否则返回 ba && b 如果 a 是假值则返回 a,否则返回 b。那个行为基于假值集:false0""nullundefinedNaN 是六个假值,其他一切都是真值。

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

|| 默认值技巧有一个尖锐的边缘,这就是为什么 ??(空值合并)存在。|| 在左侧是任何假值时回退,所以 0 || 1010"" || "n/a""n/a",甚至当 0"" 是你想保留的有效输入时。?? 只有当左侧是 nullundefined 时才回退,将 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 将某些值视为假值:false0""nullundefinedNaN 是假值,其他一切都是真值。这让 !name 代表"没有名字被给出"。
Juno逻辑运算符和真值&& 需要两侧都是真,|| 需要一个,! 反转。每个值也要么是真值,要么是假值,假值集足够小可以记住:false0""nullundefinedNaN。注意 "0""false" 是字符串,所以两个都是真值。
Juno逻辑运算符和真值&&|| 短路并返回一个操作数,不是一个布尔值,这就是为什么 name || "Guest" 作为默认工作。那个默认值有一个边缘:|| 替换每个假值,所以当 0"" 是你想保留的输入时使用 ??。六个假值是全部情况,所以学习那个集合,其余的就会落出来。

运算符从这里何去何从

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

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