主题
运算符(Day2 4/4)
本页对应 Day02_JavaScript,共 5 段视频,约 46 分钟。
学习目标
- 能说明赋值运算符的操作数个数、左操作数必须是变量、右操作数类型不限,并判断赋值表达式的值及其副作用。
- 能正确使用
+=、-=、*=、/=、%=等复合赋值运算符,将其改写为等价展开形式并预测变量与表达式的值。 - 能说出位运算符按二进制位运算的基本规则,使用
&和|对简单整数进行按位与、按位或计算并解释结果。 - 能使用
typeof判断数据的基本类型,说明其操作数个数、返回值类型和无副作用,并正确书写以避免语法歧义。 - 能使用逗号运算符在同一语句中声明或修改多个变量,区分逗号运算符与函数参数分隔符,并判断逗号表达式的结果。
- 能根据操作数中是否含字符串,区分
+的相加、正号、字符串连接作用,正确预测字符串连接及+=字符串连接赋值的结果。
知识点
赋值运算符:等号不是“判等”,而是“赋值”
转写里的“负值运算符”是口误,准确叫赋值运算符 =。为什么需要它?因为程序要能把一个值保存到变量里,后续才能修改、计算。怎么想?把 = 看成一个二元运算符:左边是变量,右边是数据、变量或表达式;运算结果是“赋值后左边变量的值”,并且它会修改左边变量,所以有副作用。怎么做?
js
let number = 150;
number = 600;
console.log(number = "hello"); // "hello"
console.log(number); // "hello"这里 number = "hello" 不只是“把 hello 给 number”,它整体也是一个表达式,表达式的值就是赋值后的 "hello"。实际开发中我们 99% 只用它的副作用,不取表达式值。易错点:左边必须是变量,100 = 200 不合法;另外 let number = 600 是声明语句,不要把它当成可随意嵌套输出的表达式。转写中“带把的”应理解为带声明关键字的语句形式,语句和表达式要区分。
复合赋值运算符:+= 只是简写
为什么需要?n = n + 1 写起来啰嗦,于是有 +=、-=、*=、/=、%=。怎么想?n += 1 等价于 n = n + 1,n -= 5 * 10 等价于 n = n - (5 * 10),右边整体先算。怎么做?
js
let n = 100;
n += 1; // 101
n += 2; // 103
n -= 5 * 10; // 53
n %= 8; // 5
console.log(n); // 5它同样有副作用,修改的是左边变量;表达式值也是赋值后左边变量的值:
js
let m = 5;
let r = (m *= 2);
console.log(r, m); // 10 10易错点:左边仍必须是变量,右边无所谓;不熟时可在注释里写展开式,例如 n %= 8; // n = n % 8。
位运算符:把数转成二进制后逐位算
为什么需要?转写里说得很清楚:JS 应用基本不用,但要知道它存在,因为很多语言有这类底层运算。怎么想?& 是按位与,| 是按位或,操作数先转二进制,再逐位对齐计算。怎么做?
js
let n1 = 11, n2 = 7;
console.log(n1 & n2); // 3
console.log(n1 | n2); // 15推导:11 是 1011,7 是 0111。按位与得 0011,即 3;按位或得 1111,即 15。转写里的“暗慰语”应改为“按位与”,“备货”应改为“按位或”。易错点:& 和逻辑与 && 不是一回事,| 和 || 也不是一回事;不要用算术直觉猜结果。
typeof:一元类型判断
为什么需要?有时要判断一个数据是什么类型。怎么想?typeof 是一元运算符,只接一个操作数,返回表示类型的字符串,且没有副作用。怎么做?
js
let res = typeof 100;
console.log(res); // "number"
console.log(typeof "abc"); // "string"转写里的“开播”就是 typeof。易错点:typeof 100 中间要有空格,或写 typeof(100);写成 typeof100 会被当成一个变量名而报错。括号的主要作用是隔开运算符和操作数,并不是必须“函数式调用”。
逗号运算符:把多个表达式串起来
为什么需要?想在一个表达式位置依次执行多个赋值。怎么想?逗号运算符有两个操作数,无类型要求,无副作用;整个表达式的值取最后一个操作数的值。怎么做?
js
let a = 100, b = 200, c = 300; // 声明里的逗号是分隔符
a = 1000, c = 2000;
console.log(a, b, c); // 1000 200 2000
let result = (a = 1000, c = 2000);
console.log(result); // 2000易错点:console.log(1000, 2000) 里的逗号是参数分隔符,不是逗号运算符;要让它成为整体表达式,可加小括号。声明 let a = 100, b = 200 里的逗号也是声明分隔符,不要和逗号运算符混为一谈。
加号的三副面孔:正号、加法、字符串连接
为什么需要?+ 既能做加法,又能连接字符串,还能作一元正号。怎么想?先看操作数个数:一个操作数是正号;两个操作数时,只要有一个是字符串,就是字符串连接,否则做加法。怎么做?
js
console.log(45 + "8"); // "458"
console.log(1000 + "hello"); // "1000hello"
console.log("hello" + 1000); // "hello1000"
console.log(true + false); // 1
let num = 200;
num += "hello";
console.log(num); // "200hello"转写里的“死针”应改为“字符串(string)”。易错点:45 + "8" 不是 53,而是字符串 "458";+= 也遵循同样规则,只要右边出现字符串,就会变成字符串连接赋值。判断 + 的作用时,先数操作数,再看有没有字符串。
题目回顾:先别急着算,先看运算符
本节作业是写出下面代码的输出结果:
js
var n = 100;
console.log(n ++ && n --);
console.log(n);
console.log(-- n || n ++);
console.log(n);这道题表面上看只是 ++、--、&&、|| 的组合,实际考的是三个基础点:第一,自增自减会修改变量,也就是转写稿里反复强调的“副作用”;第二,自增自减组成的表达式本身有值;第三,&& 和 || 会发生短路,后面的代码不一定执行。转写稿里讲赋值运算符时说得很清楚:一个表达式运算完之后,要看两个操作数有没有被修改,被修改了就叫有副作用;并且表达式整体也有值。自增自减同样要按这个思路分析。
先建立判断顺序:先取值,再改值,还是先改值,再取值
n++ 和 n-- 是“先用后改”:表达式的值取变量原来的值,然后变量再加一或减一。--n 是“先改后用”:变量先减一,表达式的值取减完后的新值。
题目开始时:
js
var n = 100;所以此时 n 是 100。后面每一步都要同时记录两个东西:表达式当前的值,以及 n 被改成了多少。不能只看 n 最后是多少,因为 console.log 输出的是整个表达式的值。
第一条:n++ && n--
js
console.log(n ++ && n --);先看左边 n++:它取 n 原来的值 100 作为表达式的值,然后 n 变成 101。所以左边得到 100。在 JavaScript 中,100 是真值,&& 左边为真时会继续看右边,并且整个 && 表达式的值取右边操作数的值。
再看右边 n--:此时 n 是 101,n-- 先取 101 作为表达式的值,然后 n 变成 100。因此右边表达式的值是 101,整个 n++ && n-- 的值就是 101。
所以第一条输出:
js
101这里最容易错的地方是:有人算出 n 最后变成 100,就以为输出 100;也有人把 && 当成只输出 true。但 && 返回的是操作数的值,不是简单的布尔值。本题右边是 101,所以输出 101。
第二条:输出 n
js
console.log(n);经过第一条后,n 先由 100 变成 101,再由 101 变成 100。所以第二条输出:
js
100这一步是在检查你有没有跟踪副作用。转写稿里说“副作用指的是运算完成之后,操作数有没有被修改”,这里被修改的操作数就是变量 n。
第三条:--n || n++
js
console.log(-- n || n ++);此时 n 是 100。先算左边 --n:这是前置自减,先减再取值,所以 n 先变成 99,整个 --n 的值是 99。99 是真值,|| 左边为真时会发生短路,右边的 n++ 不再执行。
因此整个 --n || n++ 的值取左边的值 99,输出:
js
99注意:因为短路,n++ 没有执行,所以 n 仍然是 99,不会因为右边写了个 n++ 就变成 100。这就是 || 的短路特性,也是本题第二个易错点。
第四条:再次输出 n
js
console.log(n);第三条中 --n 已经把 n 从 100 改成 99,而右边的 n++ 被短路没有执行,所以最后 n 还是 99。输出:
js
99最终答案与检查表
完整输出结果是:
js
101
100
99
99可以把本题的判断规则压缩成四句话:
n++、n--:先取原值,再修改n。--n:先修改n,再取新值。&&:左边为真看右边,整个表达式的值取右边;左边为假直接返回左边。||:左边为真直接返回左边并短路;左边为假才看右边。
易错点提醒
第一,不要把 &&、|| 的结果一律写成 true 或 false。本题中它们返回的是操作数的值,所以会出现 101、99。第二,不要忽略自增自减对 n 的修改,每执行一次都要更新 n。第三,看到 || 左边已经为真,就要立刻停止,右边不执行。第四,转写稿里提到自增自减这类有副作用的运算符,操作数必须是变量形式,不能写成 100++ 这种形式;本题操作数都是变量 n,语法没有问题。把“表达式值”和“变量最终值”分开记录,这道作业就不会绕晕。
课堂画面





代码示例
1. 作业题:自增自减与逻辑运算
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>作业:自增自减与逻辑运算</title>
</head>
<body>
<pre id="output"></pre>
<script>
const output = document.getElementById('output');
function log(value) {
output.textContent += value + '\n';
console.log(value);
}
var n = 100;
log(n ++ && n --);
log(n);
log(-- n || n ++);
log(n);
</script>
</body>
</html>输出结果:
text
101
100
99
99要点解释:
n ++ && n --:n++先返回100,n变成101;100是真值,继续执行n--,n--返回101,n变成100,整个&&表达式的值是101。console.log(n):此时n是100。-- n || n ++:--n先把n减为99,并返回99;99是真值,||发生短路,右边的n++不执行,整个表达式值是99。console.log(n):因为n++没有执行,所以n仍是99。- 易错点:
&&和||返回的不一定是布尔值,而是参与运算的操作数本身;后缀n--返回的是减之前的值,前缀--n返回的是减之后的值。
2. 赋值运算符、复合赋值、字符串连接与逗号运算符
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>赋值运算符与字符串连接</title>
</head>
<body>
<pre id="output"></pre>
<script>
const output = document.getElementById('output');
function log(value) {
output.textContent += value + '\n';
console.log(value);
}
var number = 150;
number = 600;
number = 'hello';
log(number);
var n = 100;
n += 1;
log(n);
n += 2;
log(n);
n -= 5 * 10;
log(n);
n %= 8;
log(n);
var a = 5;
log(a *= 2);
log(a);
var A = 1000;
log(A + 'hello');
log(45 + '8');
log(1 + 2);
var B = 200;
B += 'hello';
log(B);
var x = 100, y = 200, z = 300;
x = 1000, z = 2000;
log(x);
log(z);
var res = (x = 1000, z = 2000);
log(res);
console.log(1000, 200);
</script>
</body>
</html>输出结果:
text
hello
101
103
53
5
10
10
1000hello
458
3
200hello
1000
2000
2000要点解释:
number = 600、number = 'hello':赋值运算符左侧必须是变量,右侧可以是直接量、变量或表达式。n += 1等价于n = n + 1;n += 2等价于n = n + 2。n -= 5 * 10等价于n = n - (5 * 10),右侧先计算为50,所以103 - 50 = 53。n %= 8等价于n = n % 8,53 % 8 = 5。a *= 2:执行前a是5,执行后a是10,整个赋值表达式的值也是10。A + 'hello'、45 + '8':只要+两侧有一个操作数是字符串,就执行字符串连接,数字会被转成字符串,所以45 + '8'是'458',不是53。1 + 2:两个操作数都不是字符串,执行数字相加,结果是3。B += 'hello':+=同样遵循字符串连接规则,结果是'200hello'。var res = (x = 1000, z = 2000):逗号运算符返回最后一个表达式的值,所以res是2000。console.log(1000, 200):函数小括号里的逗号是参数分隔符,不是逗号运算符。- 易错点:复合赋值左侧必须是变量,不能写成
100 += 1;字符串连接时不要把'8'自动当成数字8。
3. 位运算符与 typeof
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>位运算符与 typeof</title>
</head>
<body>
<pre id="output"></pre>
<script>
const output = document.getElementById('output');
function log(value) {
output.textContent += value + '\n';
console.log(value);
}
var n1 = 11;
var n2 = 7;
log(n1 & n2);
log(n1 | n2);
var res = typeof 100;
log(res);
log(typeof 'hello');
// 易错写法,会报错,不要取消注释:
// console.log(typeof100);
</script>
</body>
</html>输出结果:
text
3
15
number
string要点解释:
n1 & n2:按位与。11的二进制是1011,7的二进制补成0111,逐位与得到0011,即3。n1 | n2:按位或。1011 | 0111得到1111,即15。typeof 100:typeof是一元运算符,用来判断数据类型,表达式值是字符串'number'。typeof 'hello':结果是字符串'string'。typeof标准写法是typeof 100或typeof(100),运算符和操作数之间要留空格或用小括号隔开。- 易错点:写成
typeof100时,浏览器会把typeof100当成一个变量名,导致语法错误。 - 位运算符在实际 JS 业务开发中很少使用,这里重点理解“先转二进制,再逐位运算”的规则。
常见错误
现象:
console.log(var n = 100)报语法错误。
原因:var n = 100是声明语句,不能整体当表达式输出。
修法:声明与输出分开写,如var n = 100; console.log(n);。现象:
n++ && n--的n最终值算错。
原因:忽略逻辑与短路,且未记录++/--返回旧值与副作用后的新值。
修法:先标当前值,再判断左操作数真假;若短路,右侧不执行。现象:写
100 += 1或5++报错。
原因:复合赋值和自增自减会对左操作数产生副作用,左操作数必须是变量。
修法:左侧改为变量,如var n = 100; n += 1;。现象:
typeof100被当成变量名或报错。
原因:typeof与操作数连写后解析异常。
修法:写成typeof 100或typeof(100)。现象:
console.log(a = 1, b = 2)以为输出逗号表达式的值。
原因:函数小括号内的逗号是参数分隔符,不是逗号运算符。
修法:若要整体作为逗号表达式,加括号:console.log((a = 1, b = 2));。现象:把
45 + "8"算成53。
原因:二元+中只要有一个操作数是字符串,就执行字符串连接。
修法:需要数值相加时显式转换,如45 + Number("8")。
练习
- 写出输出结果:
js
var a = 5;
console.log(a += 2 * 3);
console.log(a);答案:11、11。
- 写出输出结果:
js
var b = 10;
console.log(b++ || b--);
console.log(b);答案:10、11。
- 写出输出结果:
js
console.log(typeof 100);
console.log(typeof(100));
console.log(typeof "100");答案:number、number、string。
- 写出输出结果:
js
var x = 1, y = 2;
console.log((x = 10, y = 20));
console.log(x, y);答案:20、10 20。
- 写出输出结果:
js
console.log(45 + "8");
console.log(45 + 8);
console.log(11 & 7);
console.log(11 | 7);答案:458、53、3、15。
原视频对照
| # | 视频 | 时长 |
|---|---|---|
| 1 | 赋值运算符 | 23:53 |
| 2 | 位运算符 | 06:35 |
| 3 | 其他运算符-typeof | 02:44 |
| 4 | 其他运算符-逗号运算符 | 07:00 |
| 5 | 其他运算符-字符串连接符 | 05:45 |
小结
- 分析赋值类表达式时,要同时看“表达式值”和“变量被修改后的值”。
- 遇到
++/--与&&/||混合,先判断短路,再决定右侧是否执行。 - 复合赋值左侧必须是变量,右侧可以是直接量、变量或表达式。
typeof建议加空格或括号,避免连写造成解析错误。- 逗号是否为运算符要看位置,函数参数括号内优先视为参数分隔符。
- 二元
+先检查是否有字符串操作数,有则连接,无则按数值相加。 - 位运算了解规则即可,不要与逻辑运算混淆。
- 做题时养成“拆表达式、标当前值、按优先级和短路逐步计算”的习惯。