Skip to content

数据类型转换与运算符(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 == '');      // false

null 是著名坑:它和 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

课堂画面 2

课堂画面 3

课堂画面 4

课堂画面 5

课堂画面 6

代码示例 ​

示例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,可能是操作数本身;自动转布尔只用于判断,不会改变原操作数。
  • 易错点:短路会导致后面的代码不执行,所以自增、自减、函数调用等可能“没有机会执行”。

常见错误 ​

  1. 现象:自增自减混合表达式,表达式值算对但最终变量值算错。
    原因:只记“加了几次/减了几次”,没有逐步记录“本步取值”和“变量副作用”。
    修法:每题画两列:表达式取值、变量当前值,严格从左到右逐项推进。

  2. 现象:看到字符串就按字符串规则比较,导致 'A' >= 97 判错。
    原因:忽略“只有两个操作数同为字符串”才按字符串规则比较。
    修法:比较前先判断两边是否同为字符串;不是则按数值转换规则处理。

  3. 现象:'HL' > 'Hello'、'10' < '9' 这类题凭长度或语感判断。
    原因:没有逐字符比较,也没有注意首个不同字符就决定结果。
    修法:逐位标出 Unicode/字符顺序,遇到第一个不同字符立即定结果,后续不再看。

  4. 现象:null == 0、null == ''、null == undefined 混用同一套转换思路。
    原因:忽略 null 的宽松判等特例,把 == 当成普通数值转换。
    修法:判空统一用 === null / === undefined 或显式组合判断;不要靠 == 推。

  5. 现象:逻辑与/或表达式的结果误写成 true/false,且右侧副作用是否执行判断错。
    原因:把逻辑运算符当成“强制返回布尔值”,忽略短路和“返回操作数本身”。
    修法:先判断左侧真假,再决定是否执行右侧;结果按“取哪个操作数”记录,不强行转布尔。

  6. 现象:业务判等大量使用 ==,出现 '1' == 1、0 == '' 等隐式转换问题。
    原因:图省事,默认让运行时做类型转换。
    修法:默认使用 === / !==;只有明确需要类型转换时才使用 ==,并加注释说明。

练习 ​

  1. 设 let n = 10;,求 let r = n++ - n-- + ++n + --n + n++; 后 r 与 n 的值。
    答案:r = 30,n = 11。

  2. 求下列表达式结果:
    'A' >= 97、'10' < '9'、null == 0、null === undefined、null == undefined。
    答案:false、true、false、false、true。

  3. 设 let a = 0;,求 let r = a++ && ++a; 后 r 与 a 的值。
    答案:r = 0,a = 1。

  4. 设 let b = 0;,求 let s = b++ || ++b; 后 s 与 b 的值。
    答案:s = 2,b = 2。

  5. 判断结果:'' == 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

小结 ​

  1. 自增自减题先分离“表达式值”和“变量副作用”,不要混算。
  2. 比较大小先看是否“两边同为字符串”;只有同为字符串才逐字符比较。
  3. 非同为字符串的比大小会走数值转换;涉及 NaN 的比较通常不成立。
  4. == 会做类型转换,=== 不做;工程代码默认优先 === / !==。
  5. null 的宽松判等是特例,不能按普通数值转换推导,判空要显式写。
  6. 逻辑与/或返回操作数本身,并可能短路;右侧是否执行要看左侧真假。
  7. 含副作用的复杂表达式可读性差,实际开发应拆成多步语句。
  8. 这类题用于训练逻辑和避坑,生产代码优先严格判等、清晰表达和可维护性。