主题
数据类型转换与运算符(Day2 3/4)
本页对应 Day02_JavaScript,共 6 段视频,约 1 小时 6 分。
学习目标
- 能够正确计算包含前置/后置
++、--的复合表达式,并说明变量最终值与表达式取值的变化过程。 - 能够使用
>、<、>=、<=判断数字、布尔值、null、Infinity及字符串的大小关系,并解释其类型转换规则。 - 能够区分字符串按 Unicode 编码逐字符比较的规则,并正确判断多字符字符串的大小关系。
- 能够区分
==与===、!=与!==的判等规则,正确解释类型转换、null的特殊判等结果及推荐使用全等的原因。 - 能够根据逻辑与
&&、逻辑或||的短路求值规则,推导表达式返回值、变量副作用及后续操作数是否执行。 - 能够使用逻辑非
!对操作数进行布尔取反,并组合关系运算符与逻辑运算符编写年龄区间等条件判断表达式。
知识点
先看累加累减:表达式值不等于变量最终值
数据类型转换常和“表达式取值”混在一起考。先复习 ++、--:它们会改变变量,也会产生一个表达式值。变量最终值看执行了几次;表达式值看运算符在前还是在后。
js
let number = 10;
let result = number++ - number-- + ++number + --number + number++;
console.log(result); // 30
console.log(number); // 11推导时逐步看:number++ 取加之前的值 10,变量变 11;number-- 取减之前的值 11,变量变 10;++number 取加之后的值 11;--number 取减之后的值 10;最后的 number++ 取 10。表达式是 10 - 11 + 11 + 10 + 10,结果为 30。这里要分清“变量被副作用改了”和“表达式当时取到哪个值”,后面判断转换时同样要分清“转换用于运算”与“操作数本身有没有变”。
关系运算符比大小:先判断类型,再决定转不转
为什么需要它?因为程序经常要比较年龄、分数、价格。怎么想?先问两个操作数是不是都为数字;不是数字时,大多数情况会转成 number 再比。怎么做?
js
console.log(100 > 20); // true
console.log(67 <= 30); // false
console.log(true > false); // true,转成 1 > 0
console.log('' <= 0); // true,空字符串转 0
console.log('' < 100); // true
console.log('hello' > 0); // false,'hello' 转 number 为 NaN关键特例:只有两个操作数都是字符串,才按字符串规则比较,不转 number。字符串比较是逐字符比较 Unicode 编码,只要某一位分出大小,整个结果就定了,后面不再看。
js
console.log('C' > 'A'); // true,C 的编码 99 > A 的编码 65
console.log('HL' > 'HELLO'); // true,H 相同,L 的编码大于 E
console.log('A' >= 97); // false,只有一个字符串,A 转 number 为 NaN易错点:'A' >= 97 不是按 'A' 的编码 65 和 97 比,因为两个操作数不都是字符串;只有一个字符串时仍走数字转换。还有 null、undefined 参与大小比较很绕,课堂演示也强调它们“没有正常可比性”,实际开发中不要依赖这类边界,判等优先用全等。
判等:== 会转换,=== 不转换
为什么需要?因为要判断两个值是否相等。怎么想?== 在类型不一致时可能转 number 再比;=== 要求类型和值都一致,类型不同直接 false。怎么做?
js
console.log('' == false); // true,都转成 0
console.log('1' == 1); // true
console.log('1' === 1); // false,类型不同
console.log(null == undefined); // true
console.log(null == 0); // false,特殊规则
console.log(null == false); // false
console.log(null == ''); // falsenull 是著名坑:它和 undefined 判等为 true,但和 0、false、空字符串用 == 判等却是 false。课堂里说这是设计遗留问题,不建议花大量时间推原因,记住现象即可。易错点:== 的结果可能受隐式转换影响;=== 更严格,日常判等优先 ===,确实需要类型转换时再用 ==。
逻辑与 &&:短路,并返回操作数本身
为什么需要?组合多个条件,比如年龄必须“大于等于 18 并且小于等于 60”。怎么想?&& 先看第一个操作数转布尔后是否成立;不成立就取第一个操作数作为整个表达式值,第二个不执行;成立才看第二个,并取第二个操作数作为表达式值。怎么做?
js
console.log(true && 250); // 250
console.log('' && 300); // '',第一个不成立,后面不执行
let n = 0;
let res = n++ && ++n;
console.log(res); // 0
console.log(n); // 1推导:n++ 取 0,n 变 1;0 转布尔为 false,发生短路,++n 不执行;整个表达式取第一个操作数 0,所以 res 是 0,n 只加了一次。易错点:&& 的结果不一定是布尔值,它返回的是某个操作数;并且第二个操作数可能根本不执行。
逻辑或 || 与逻辑非 !
|| 与 && 相反:第一个成立就取第一个,第二个不执行;第一个不成立才取第二个。
js
console.log('hello' || 250); // 'hello'
console.log('' || 300); // 300
let m = 0;
let r = m++ || ++m;
console.log(r); // 2
console.log(m); // 2推导:m++ 取 0,m 变 1,0 不成立;执行 ++m,m 变 2,取 2 作为结果,所以 r 为 2,m 为 2。! 则总是把操作数转布尔后取反,结果是布尔值:
js
console.log(!true); // false
console.log(!100); // false
console.log(!''); // true易错点:&&、|| 返回操作数本身,! 返回布尔;逻辑运算中的类型转换只用于判断,不会真的把原变量改成另一种类型。
1. 累加累减:先看取值,再看副作用
为什么需要:++、-- 放在变量前后会影响“表达式取值”,而变量本身也会变化,初学最容易在这里绕晕。怎么想:把每一步拆开,先写当前变量值,再判断取加/减之前还是之后的值。怎么做:以初始 number=10 为例,从左到右演算:
js
let number = 10;
let result = number++ - number-- + ++number + --number + number++;
// number++ 取10,number变11
// number-- 取11,number变10
// ++number 取11,number变11
// --number 取10,number变10
// number++ 取10,number变11
console.log(result); // 30
console.log(number); // 11易错点:不要只算最终变量值,还要算表达式值;前缀取变化后的值,后缀取变化前的值;副作用只要执行到就会发生。
2. 关系运算符:比大小
为什么需要:程序经常要判断“是否大于、小于、在区间内”。怎么想:先看两个操作数类型;通常转成数字再比,但两个都是字符串时按字符串规则比。怎么做:
js
console.log(100 > 20); // true
console.log(67 <= 30); // false
console.log(100 > NaN); // false
console.log(100 < Infinity); // true
console.log(true > false); // true(1 > 0)
console.log("" <= 0); // true(0 <= 0)
console.log("C" > "A"); // true(99 > 97)
console.log("HL" > "HELLO"); // true(H同,L > E)
console.log("A" >= 97); // false(A转数字为NaN)易错点:只有“两个操作数都是字符串”才逐字符按 Unicode 编码比,一旦分出大小就停止;NaN 与任何数比大小都不成立;"A" >= 97 不是按字符编码,因为右边是数字,会转数字比较。
3. 判等:== 与 ===
为什么需要:判断两个值是否相等,但“宽松相等”和“全等”规则不同。怎么想:== 类型不一致时可能转数字再比;=== 类型不一致直接不成立。怎么做:
js
console.log(false == ""); // true
console.log(null == ""); // false
console.log(null == 0); // false
console.log(null == undefined); // true
console.log("" === false); // false
console.log(189 === "189"); // false易错点:null 的判等很特殊,不能套普通转换规则;=== 不发生自动类型转换;实际开发优先用 ===,更严格、更少意外。
4. 逻辑与:短路并返回操作数
为什么需要:组合多个条件,例如“年龄大于等于18并且小于等于60”。怎么想:先把第一个操作数转布尔判断;若为假,整个表达式取第一个操作数,第二个不执行;若为真,取第二个操作数。怎么做:
js
console.log(true && 250); // 250
console.log("" && 300); // ""
let n = 0;
let res = n++ && ++n;
console.log(res, n); // 0 1易错点:逻辑与的表达式值不一定是布尔值,而是两个操作数之一;第一个为假时第二个不执行,所以 ++n 没跑,n 只加了一次。
5. 逻辑或:短路并返回操作数
为什么需要:表示“满足任一条件即可”。怎么想:第一个为真就取第一个并短路;第一个为假才看第二个,并取第二个。怎么做:
js
console.log("hello" || 250); // "hello"
console.log("" || 300); // 300
let m = 0;
let r = m++ || ++m;
console.log(r, m); // 2 2易错点:不要以为结果总是 true/false;第一个为真时第二个不执行;本例中 m++ 取0为假,才执行 ++m,所以 m 变成2。
6. 逻辑非:取反但不改原值
为什么需要:把条件结果反过来。怎么想:先把操作数转布尔,再取反。怎么做:
js
console.log(!true); // false
console.log(!100); // false(100转布尔为true)
console.log(!""); // true(空串转布尔为false)易错点:逻辑非没有副作用,原操作数不变;它返回的是取反后的布尔值。
课堂画面






代码示例
示例1:自增自减表达式与变量副作用
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>自增自减表达式</title>
</head>
<body>
<pre id="out"></pre>
<script>
const out = document.getElementById('out');
function log(label, value) {
out.textContent += label + ': ' + value + '\n';
}
let number = 10;
// 后置:先取旧值,再自增/自减
// 前置:先自增/自减,再取新值
const result =
number++ - // 取10,number变成11
number-- + // 取11,number变成10
++number + // number变成11,取11
--number + // number变成10,取10
number++; // 取10,number变成11
log('表达式值', result); // 30
log('number最终值', number); // 11
</script>
</body>
</html>关键行与易错点:
number++是后置自增:表达式取自增前的值,但变量本身仍然会加 1。++number是前置自增:变量先加 1,表达式再取新值。- 易错点:不要把“表达式的值”和“变量最终的值”混在一起算。整个表达式算完后,
number已经变成11。 - 易错点:前置和后置都会产生副作用,区别只在于表达式取值时是取变化前还是变化后的值。
示例2:关系运算符比大小与判等
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>关系运算符</title>
</head>
<body>
<pre id="out"></pre>
<script>
const out = document.getElementById('out');
function log(label, value) {
out.textContent += label + ': ' + value + '\n';
}
// 比大小
log('100 > 20', 100 > 20); // true
log('67 <= 30', 67 <= 30); // false
log('100 > NaN', 100 > NaN); // false
log('100 < Infinity', 100 < Infinity); // true
log('true > false', true > false); // true
log("'' <= 0", '' <= 0); // true
log("'hello' > undefined", 'hello' > undefined); // false
log("'C' > 'A'", 'C' > 'A'); // true
log("'HL' > 'HELLO'", 'HL' > 'HELLO'); // true
log("'A' >= 97", 'A' >= 97); // false
// 判等
log("false == ''", false == ''); // true
log("false === ''", false === ''); // false
log("'1' == 1", '1' == 1); // true
log("'1' === 1", '1' === 1); // false
log('null == undefined', null == undefined); // true
log('null == 0', null == 0); // false
log("null == ''", null == ''); // false
log('null == false', null == false); // false
log('null === undefined', null === undefined); // false
log('NaN == NaN', NaN == NaN); // false
log("'hello' != 'hello'", 'hello' != 'hello'); // false
</script>
</body>
</html>关键行与易错点:
- 比大小时,如果两个操作数不都是字符串,通常会转成数字再比较;
'A' >= 97中'A'转数字是NaN,所以结果是false。 - 如果两个操作数都是字符串,会按字符的 Unicode 编码逐个比较。
'HL' > 'HELLO'先比第一个字符H,相同;再比第二个字符L和E,L更大,所以整体为true。 ==在类型不一致时会做类型转换;===要求类型和值都一致,类型不一致直接为false。- 易错点:
null == 0是false,不是true;null == undefined是true,但null === undefined是false。 - 易错点:
NaN不等于自身,所以NaN == NaN是false。
示例3:逻辑运算符与短路求值
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>逻辑运算符与短路</title>
</head>
<body>
<pre id="out"></pre>
<script>
const out = document.getElementById('out');
function log(label, value) {
out.textContent += label + ': ' + value + '\n';
}
let n = 0;
// &&:第一个操作数转布尔为 false,直接返回第一个操作数,第二个不执行
let res1 = n++ && ++n;
log('res1', res1); // 0
log('n after &&', n); // 1
n = 0;
// ||:第一个操作数转布尔为 false,继续执行第二个操作数
let res2 = n++ || ++n;
log('res2', res2); // 2
log('n after ||', n); // 2
// 逻辑运算表达式的值不一定是布尔值
log('true && 250', true && 250); // 250
log("'' && 300", '' && 300); // ''
log("'hello' || 250", 'hello' || 250); // hello
log("'' || 300", '' || 300); // 300
// 逻辑非:先转布尔,再取反
log('!true', !true); // false
log('!false', !false); // true
log('!100', !100); // false
log("!''", !''); // true
</script>
</body>
</html>关键行与易错点:
&&的规则:第一个操作数转布尔为假,就返回第一个操作数,并且第二个操作数不执行;第一个为真,则返回第二个操作数。||的规则:第一个操作数转布尔为真,就返回第一个操作数,并且第二个操作数不执行;第一个为假,则返回第二个操作数。n++ && ++n中,n++取0,0转布尔为false,所以&&直接返回0,后面的++n不执行,因此n只加了一次,结果是1。n++ || ++n中,n++取0,转布尔为false,所以继续执行++n,此时n先变成2,表达式返回2。- 易错点:逻辑运算表达式的值不一定是
true/false,可能是操作数本身;自动转布尔只用于判断,不会改变原操作数。 - 易错点:短路会导致后面的代码不执行,所以自增、自减、函数调用等可能“没有机会执行”。
常见错误
现象:自增自减混合表达式,表达式值算对但最终变量值算错。
原因:只记“加了几次/减了几次”,没有逐步记录“本步取值”和“变量副作用”。
修法:每题画两列:表达式取值、变量当前值,严格从左到右逐项推进。现象:看到字符串就按字符串规则比较,导致
'A' >= 97判错。
原因:忽略“只有两个操作数同为字符串”才按字符串规则比较。
修法:比较前先判断两边是否同为字符串;不是则按数值转换规则处理。现象:
'HL' > 'Hello'、'10' < '9'这类题凭长度或语感判断。
原因:没有逐字符比较,也没有注意首个不同字符就决定结果。
修法:逐位标出 Unicode/字符顺序,遇到第一个不同字符立即定结果,后续不再看。现象:
null == 0、null == ''、null == undefined混用同一套转换思路。
原因:忽略null的宽松判等特例,把==当成普通数值转换。
修法:判空统一用=== null/=== undefined或显式组合判断;不要靠==推。现象:逻辑与/或表达式的结果误写成
true/false,且右侧副作用是否执行判断错。
原因:把逻辑运算符当成“强制返回布尔值”,忽略短路和“返回操作数本身”。
修法:先判断左侧真假,再决定是否执行右侧;结果按“取哪个操作数”记录,不强行转布尔。现象:业务判等大量使用
==,出现'1' == 1、0 == ''等隐式转换问题。
原因:图省事,默认让运行时做类型转换。
修法:默认使用===/!==;只有明确需要类型转换时才使用==,并加注释说明。
练习
设
let n = 10;,求let r = n++ - n-- + ++n + --n + n++;后r与n的值。
答案:r = 30,n = 11。求下列表达式结果:
'A' >= 97、'10' < '9'、null == 0、null === undefined、null == undefined。
答案:false、true、false、false、true。设
let a = 0;,求let r = a++ && ++a;后r与a的值。
答案:r = 0,a = 1。设
let b = 0;,求let s = b++ || ++b;后s与b的值。
答案:s = 2,b = 2。判断结果:
'' == false、'1' === 1、NaN == NaN、NaN === NaN。
答案:true、false、false、false。
原视频对照
| # | 视频 | 时长 |
|---|---|---|
| 1 | 累加累减题目计算 | 03:51 |
| 2 | 关系运算符-比大小 | 18:46 |
| 3 | 关系运算符-判等 | 14:30 |
| 4 | 逻辑运算符-逻辑与 | 21:13 |
| 5 | 逻辑运算符-逻辑或 | 05:44 |
| 6 | 逻辑运算符-逻辑非 | 02:45 |
小结
- 自增自减题先分离“表达式值”和“变量副作用”,不要混算。
- 比较大小先看是否“两边同为字符串”;只有同为字符串才逐字符比较。
- 非同为字符串的比大小会走数值转换;涉及
NaN的比较通常不成立。 ==会做类型转换,===不做;工程代码默认优先===/!==。null的宽松判等是特例,不能按普通数值转换推导,判空要显式写。- 逻辑与/或返回操作数本身,并可能短路;右侧是否执行要看左侧真假。
- 含副作用的复杂表达式可读性差,实际开发应拆成多步语句。
- 这类题用于训练逻辑和避坑,生产代码优先严格判等、清晰表达和可维护性。