Skip to content

DRAFT 《现代 JavaScript 教程》前端基础系列 03:语言进阶——类型转换、运算符与控制流

约 6942 字大约 23 分钟

2026-04-17

本篇我们将深入探讨 JavaScript 中类型转换的规则,探索基础的数学及逻辑运算符,并学习如何使用条件分支控制代码的走向。掌握这些机制将帮助你避免因隐式转换带来的陷阱,并编写出更加严谨的代码。

【本篇核心收获】

  • 理解 JavaScript 中的三种基本类型转换(字符串、数字、布尔值)及其陷阱。
  • 掌握算术运算符、一元运算符与赋值运算符的常见特性及优先级。
  • 熟悉各种值的比较规则,特别是与 nullundefined 比较时的特例。
  • 灵活运用 if...else 语句及三元运算符 ? 进行条件分支控制。
  • 深入理解逻辑运算符 ||(或)、&&(与)、!(非)的短路求值特性。

类型转换:揭开隐式转换的面纱

在实际开发中,运算符和函数往往会自动将赋予它们的值转换为正确的类型。例如,alert 会自动将任何值转换为字符串以进行显示,而算术运算符则会将值转换为数字。但在某些场景下,我们需要手动且显式地转换值的类型。

注:本节暂不讨论 object(对象)类型,仅聚焦于原始值的转换。

字符串转换 (String Conversion)

背景与认知: 当我们需要一个值的字符串形式时,就会发生字符串转换。最常见的场景就是输出或者显示内容。

落地操作:
你可以通过显式调用 String(value) 来将值转换为字符串。

let value = true;
alert(typeof value); // 输出: "boolean"

// 显式转换为字符串
value = String(value); 
alert(typeof value); // 输出: "string" 
// 此时,原来的布尔值 true 变成了一个字符串 "true"

验证与现象:
字符串转换的规则非常直观。false 变成 "false"null 变成 "null",依此类推。

数字型转换 (Numeric Conversion)

背景与认知: 在算术函数和表达式中,通常会自动进行数字型转换。

落地操作:
例如,当把除法 / 用于非数字类型时,会触发自动转换:

alert("6" / "2"); 
// 输出: 3
// 解析: string 类型的值被自动转换成 number 类型后进行计算

当然,我们也可以使用 Number(value) 显式地将值转换为数字:

let str = "123";
let num = Number(str); // 显式转换为 number 类型,值为 123

避坑提示: 当我们从 HTML 表单等文本源读取值,且期望它是一个数字时,往往需要这种显式转换。如果字符串不是一个有效的数字,转换的结果将是 NaN(Not a Number)。

let age = Number("an arbitrary string instead of a number");
alert(age); // 转换失败,输出 NaN

数字转换规则一览表:

转换结果
undefinedNaN
null0
true / false1 / 0
string自动去掉首尾空白字符(空格、换行符 \n、制表符 \t 等)。 如果剩余字符串为空,则结果为 0。 如果剩余字符串包含非数字字符,则结果为 NaN

验证案例:

alert(Number("   123   ")); // 123 (首尾空白被忽略)
alert(Number("123z"));      // NaN (遇到了非数字字符 "z")
alert(Number(true));        // 1
alert(Number(false));       // 0

避坑提示: 请特别留意 nullundefined 的区别:null 转换为 0,而 undefined 转换为 NaN

布尔型转换 (Boolean Conversion)

背景与认知: 这是最简单的一种转换,通常发生在逻辑运算(如条件判断)中,也可以通过调用 Boolean(value) 显式转换。

转换规则:

  1. 假值 (Falsy values):直观上为“空”或“无”的值,如 0、空字符串 ""nullundefinedNaN,都会变成 false
  2. 真值 (Truthy values):除了假值之外的所有值,都会变成 true

验证案例:

alert(Boolean(1));       // true
alert(Boolean(0));       // false
alert(Boolean("hello")); // true
alert(Boolean(""));      // false

避坑提示:包含 0 的字符串 "0" 也是 true
在某些语言(如 PHP)中,"0" 被视为 false。但在 JavaScript 中,任何非空的字符串总是true。即使是只包含一个空格的字符串 " ",也是 true

alert(Boolean("0")); // true
alert(Boolean(" ")); // 空格也是非空字符串,所以是 true

模块小结: 类型转换分为三种:字符串转换(直观的文本化)、数字转换(常用于数学运算,注意 undefinedNaN),以及布尔转换(记住五种假值即可)。


基础运算符与数学运算

这一节我们将从学校学过的加减乘除开始,逐步过渡到 JavaScript 特有的一些运算符特性。

术语速览:一元、二元与运算元

  • 运算元 (Operand):运算符应用的对象(有时也叫“参数”)。例如 5 * 2 中的 52
  • 一元运算符 (Unary Operator):只对一个运算元起作用。例如一元负号 - 可以翻转符号:let x = 1; x = -x; // x 变成 -1
  • 二元运算符 (Binary Operator):作用于两个运算元。例如减法:y - x

常见数学运算

JavaScript 支持基本的数学运算:加 +、减 -、乘 *、除 /。此外,还有两个稍显特别的:

取余 % (Remainder)
a % b 的结果是 a 整除 b 之后的余数。

alert( 5 % 2 ); // 1 (5 除以 2 的余数是 1)
alert( 8 % 3 ); // 2

求幂 ** (Exponentiation)
a ** b 表示求 ab 次方。它也支持非整数(如求平方根)。

alert( 2 ** 3 );     // 8 (2的3次方)
alert( 4 ** (1/2) ); // 2 (相当于求 4 的平方根)

字符串连接:二元运算符 +

认知与现象: 通常 + 用于数字求和。但只要其中一个运算元是字符串,+ 就会将两个运算元进行字符串拼接

alert( '1' + 2 ); // "12"
alert( 2 + '1' ); // "21"

深入拆解:执行顺序很重要
运算符是按顺序工作的,如果存在连续的加法,注意先后顺序!

alert( 2 + 2 + '1' ); // "41" (前两个 2 是数字相加得 4,然后再与 '1' 拼接)
alert( '1' + 2 + 2 ); // "122" (第一个是字符串,所以后续的 2 都被视为字符串拼接)

避坑提示: 只有 + 支持字符串拼接!其他算术运算符(-/*)总是将运算元隐式转换为数字后进行计算。

alert( 6 - '2' );   // 4 (字符串 '2' 被转换为数字)
alert( '6' / '2' ); // 3

数字转化:一元运算符 +

认知与落地: 除了二元的加法,+ 还可以作为一元运算符使用。如果运算元不是数字,一元加号 +强制将其转化为数字,效果等同于 Number(...),但更简短。

alert( +true ); // 1
alert( +"" );   // 0

// 常用于将字符串变量快速转化为数字进行求和
let apples = "2";
let oranges = "3";
// 在二元加号合并之前,一元加号先将它们转为数字
alert( +apples + +oranges ); // 5

运算符优先级 (Operator Precedence)

当表达式中有多个运算符时,执行顺序由优先级决定。例如乘法优先级高于加法。若想改变顺序,请使用圆括号 (),圆括号拥有最高优先级。

注意:一元运算符(如一元加号 +,优先级 15)的优先级通常高于二元运算符(如加号 +,优先级 12)。这也是为什么 +apples + +oranges 中一元加号会先起作用。

赋值运算符 = 及其返回值

赋值符号 = 也是一个运算符(优先级为 2,非常低)。这解释了为什么在 x = 2 * 2 + 1 中,计算完成后才会执行赋值。

深度拆解:赋值会返回一个值
在 JavaScript 中,所有运算符都会返回一个值。语句 x = value 不仅将 value 写入 x还会返回这个value

let a = 1;
let b = 2;

// b + 1 的结果赋给 a(a 变成 3),同时赋值表达式本身返回 3,然后再被 3 减去
let c = 3 - (a = b + 1); 

alert( a ); // 3
alert( c ); // 0

注:了解此机制即可,实际开发中为了可读性,强烈不建议写这种复杂的嵌套赋值代码。

链式赋值 (Chaining assignments)
赋值运算支持从右向左的链式操作:

let a, b, c;
a = b = c = 2 + 2; // a, b, c 都会被赋值为 4

原地修改 (Modify-in-place) 与自增/自减

原地修改
当我们需要对变量进行运算并将结果存回该变量时,可以使用如 +=*= 等缩写。

let n = 2;
n += 5; // 等同于 n = n + 5; 现在 n 是 7
n *= 2; // 等同于 n = n * 2; 现在 n 是 14

自增 (++) / 自减 (--)
用于将变量加一或减一。注意:它们只能应用于变量,不能应用于具体的数值(如 5++ 会报错)。

  • 前置形式 (++counter):先自增,然后返回新值
  • 后置形式 (counter++):先自增,但返回旧值(自增前的值)。
let counter1 = 1;
alert( ++counter1 ); // 2 (前置,返回新值)

let counter2 = 1;
alert( counter2++ ); // 1 (后置,返回旧值)
alert( counter2 );   // 2 (此时变量本身已经加一)

建议:为了代码可读性,推荐使用“一行一个行为”的模式,避免将 ++-- 与其他复杂表达式混写在一行中。

逗号运算符 , (Comma Operator)

这是一个少见且优先级极低(比 = 还低)的运算符。它允许在一行内执行多个表达式,用逗号隔开,但只会返回最后一个表达式的结果

// 注意括号,因为逗号优先级极低
let a = (1 + 2, 3 + 4); 
// 1 + 2 会执行但结果被丢弃;然后执行 3 + 4,最后返回 7 赋给 a
alert( a ); // 7

注:常用于 for 循环中的一些简写技巧,但平时应尽量少用以保证代码可读性。

模块小结: 本节我们学习了基础数学运算,认识了字符串的拼接特性(+),掌握了一元 + 的快速转型技巧,以及 ++-- 前置与后置的区别。


核心任务(练习题)

任务:后置运算符和前置运算符

以下代码中变量 abcd 的最终值分别是多少?

let a = 1, b = 1;

let c = ++a; // ?
let d = b++; // ?

解决方案:

  • a = 2 (无论前置后置,自身都会加1)
  • b = 2
  • c = 2 (前置 ++a 返回的是加 1 后的新值)
  • d = 1 (后置 b++ 返回的是加 1 前的旧值)

任务:赋值结果

下面这段代码运行完成后,代码中的 ax 的值是多少?

let a = 2;
let x = 1 + (a *= 2);

解决方案:

  • a = 4 (执行了 a *= 2,即 a = a * 2)
  • x = 5 (赋值表达式 (a *= 2) 返回了赋值的结果 4,然后计算 1 + 4)

任务:类型转换

写出下面这些表达式的结果:

"" + 1 + 0
"" - 1 + 0
true + false
6 / "3"
"2" * "3"
4 + 5 + "px"
"$" + 4 + 5
"4" - 2
"4px" - 2
"  -9  " + 5
"  -9  " - 5
null + 1
undefined + 1
" \t \n" - 2

解决方案:

"" + 1 + 0 = "10"     // (1) 空字符串开始,后续全视为字符串拼接: "1" + 0 = "10"
"" - 1 + 0 = -1       // (2) 减法强制转数字:空字符串 "" 转为 0。 0 - 1 + 0 = -1
true + false = 1      // 布尔值参与算术运算,true为1,false为0
6 / "3" = 2           // 字符串 "3" 被转为数字 3
"2" * "3" = 6         // 乘法同理,都转为数字
4 + 5 + "px" = "9px"  // 顺序执行:先算 4+5=9,然后 9 与 "px" 拼接
"$" + 4 + 5 = "$45"   // 顺序执行:开头是字符串,后续的加法全变拼接
"4" - 2 = 2           // 减法转数字
"4px" - 2 = NaN       // 减法转数字,但 "4px" 无法转换为有效数字,得到 NaN
"  -9  " + 5 = "  -9  5" // 加号拼接字符串
"  -9  " - 5 = -14    // 减号转数字,忽略首尾空格变为 -9, -9 - 5 = -14
null + 1 = 1          // 算术运算中 null 转为数字 0
undefined + 1 = NaN   // 算术运算中 undefined 转为数字 NaN,NaN与任何数相加还是 NaN
" \t \n" - 2 = -2     // 纯空白字符字符串转为数字 0, 0 - 2 = -2

任务:修正加法

下面这段代码本意是要求用户输入两个数字并显示它们的总和。但当用户输入 1 和 2 时,输出却是 12。请修正它,使结果为 3。

let a = prompt("First number?", 1);
let b = prompt("Second number?", 2);

alert(a + b); // 12

解决方案:
原因是 prompt 无论用户输入什么,都会返回一个字符串类型(即 "1""2"),使用 + 会导致字符串拼接。
我们需要在相加前将它们转换为数字。最简洁的方法是在 prompt 之前使用一元运算符 +

let a = +prompt("First number?", 1);
let b = +prompt("Second number?", 2);

alert(a + b); // 3

值的比较:真理与假象

在 JavaScript 中,比较运算符(>, <, >=, <=, ==, !=)总是返回布尔值 truefalse

字符串比较 (字典顺序)

规则: 字符串是按字符(严格说是按 Unicode 编码顺序)逐个逐位进行比较的,类似于查字典。

alert( 'Z' > 'A' );       // true (Z 的编码大于 A)
alert( 'Glow' > 'Glee' ); // true (前两个字符相同,第3个字符 'o' 大于 'e')

注:小写字母的 Unicode 编码大于大写字母,所以 'a' > 'A'true

不同类型间的比较

规则: 当对不同类型的值进行比较大小时,JavaScript 会首先将它们隐式转换为数字 (number) 然后再做判定。

alert( '2' > 1 );     // true (字符串 '2' 变成了数字 2)
alert( '01' == 1 );   // true
alert( true == 1 );   // true (true 变成了数字 1)
alert( false == 0 );  // true

严格相等 (===) 与非严格相等 (==)

背景与问题: 普通的相等检查 == 在比较前会进行类型转换。这就导致它无法区分 0false,甚至无法区分空字符串和 false

alert( 0 == false );  // true (false 会被转换为 0)
alert( '' == false ); // true ('' 和 false 都会被转换为 0)

落地操作:
如果我们想严格区分数据类型,必须使用严格相等运算符 ===(以及严格不相等 !==)。
规则: === 在进行比较时
绝对不会做任何类型转换
。如果类型不同,立刻返回 false

alert( 0 === false ); // false (数字和布尔值类型不同,不相等)

null 和 undefined 比较的特例

这部分是 JavaScript 比较逻辑中著名的“陷阱区”。

1. 严格相等 === 下:
类型不同,显然不相等。

alert( null === undefined ); // false

2. 非严格相等 == 下:
这是一条硬性特例nullundefined== 比较下就像一对好基友,它们只等于彼此,不等于任何其他值(也不发生转型)。

alert( null == undefined ); // true
alert( null == 0 );         // false (null 不等于 0)

3. 数学比较符 <, >, <=, >= 下:
此时,它们会被强制转换为数字:null 转为 0undefined 转为 NaN

著名的奇怪现象剖析:nullvs0

alert( null > 0 );  // false (null转为0,0 > 0 是错的)
alert( null == 0 ); // false (相等性检查特例,null只等于undefined)
alert( null >= 0 ); // true  (null转为0,0 >= 0 是对的)

现象解释:相等性检查 == 的机制与大小比较符 >= 的机制是完全独立的。

避坑指南:

  • 尽量使用严格相等 ===
  • 如果一个变量的值可能是 nullundefined绝对不要用它直接与数字进行 >=, <, >, <= 比较。应先单独判空。

模块小结: 比较运算始终返回布尔值。字符串按字典序比较;不同类型通常先转为数字再比较(除非使用 ===);牢记 nullundefined== 时的特例,并防范 null 在大小比较时的诡异行为。


核心任务(练习题)

任务:值的比较

写出以下表达式的执行结果:

5 > 4
"apple" > "pineapple"
"2" > "12"
undefined == null
undefined === null
null == "\n0\n"
null === +"\n0\n"

解决方案:

5 > 4                   // true
"apple" > "pineapple"   // false (字典序比较,'a' 比 'p' 小)
"2" > "12"              // true  (字典序比较首字符:'2' 大于 '1')
undefined == null       // true  (特例:它们在非严格模式下互等)
undefined === null      // false (严格模式:类型不同)
null == "\n0\n"         // false (特例:null只等于undefined,不等于其他任何值)
null === +"\n0\n"       // false (严格模式:类型不同)

条件分支:控制代码的走向 (if?)

有时候我们需要根据不同的条件让程序执行不同的任务。

if 语句与布尔转换

背景与认知: if(...) 会计算括号内的条件表达式。如果计算结果为 true(或能转换为真值),则执行对应的代码块。

let year = 2015;
// 只有当条件成立时,才会执行大括号里的内容
if (year == 2015) {
  alert( "That's correct!" );
}

隐式布尔转换:
if (...) 语句会自动将圆括号内的结果转为布尔型。结合前面学过的知识:

  • 假值 (Falsy)0, "", null, undefined, NaN。如果条件是这些,代码块不会执行。
  • 真值 (Truthy):其他所有值(如 1, "hello", "0" 等)。如果条件是这些,代码块执行。
if (0) { /* 这里面的代码永远不会执行 */ }
if (1) { /* 这里面的代码一定会执行 */ }

elseelse if 分支

if 条件不成立时,可以提供一个可选的 else 代码块作为备用方案。如果有多重条件,可以使用 else if 进行串联判断。

let year = prompt('In which year was ECMAScript-2015 specification published?', '');

if (year < 2015) {
  alert( 'Too early...' );       // 条件 1
} else if (year > 2015) {
  alert( 'Too late' );           // 条件 2
} else {
  alert( 'Exactly!' );           // 上述条件都不满足时执行
}

条件运算符(三元运算符) ?

背景与认知: 有时我们需要根据条件来给一个变量赋值。虽然可以用 if...else 解决,但代码略显冗长。

let accessAllowed;
let age = 20;

// 使用 if...else 赋值
if (age > 18) {
  accessAllowed = true;
} else {
  accessAllowed = false;
}

落地操作:
我们可以使用更简洁的“问号”运算符 ?(也称三元运算符,因为它需要三个操作数)。

语法: let result = condition ? value1 : value2;
如果 condition 为真,返回 value1;否则返回 value2

// 使用三元运算符简写上面的代码
let age = 20;
let accessAllowed = (age > 18) ? true : false;
// 注意:比较运算符 > 的优先级高于 ?,所以条件外的括号可省略。
// 实际上这里可以直接写 let accessAllowed = age > 18;

多个 ? 的链式调用:
我们可以串联多个 ? 来处理类似 else if 的多重逻辑,虽然代码紧凑,但也需注意可读性。

let age = prompt('age?', 18);

// 类似 else if 的链条检查
let message = (age < 3) ? 'Hi, baby!' :
  (age < 18) ? 'Hello!' :
  (age < 100) ? 'Greetings!' :
  'What an unusual age!';

alert( message );

避坑提示:切勿滥用 ? 替代 if
仅当你需要根据条件返回一个值时使用 ?。如果你的目的是根据条件执行不同的代码块(例如弹出不同的 alert,而不关心返回值),请老老实实使用 if...else,以保证代码的垂直可读性。

模块小结: if 语句依赖隐式布尔转换控制代码走向;else if 负责多重分支;三元运算符 ? 则是实现“条件赋值”的优雅语法糖,但不应被用作执行代码块的快捷方式。


核心任务(练习题)

任务:if(值为 0 的字符串)

alert 弹窗会出来吗?

if ("0") {
  alert( 'Hello' );
}

解决方案:
是的,它会。在 JavaScript 中,任何非空字符串(包括 "0"" ")的布尔逻辑值都是 true

任务:JavaScript 的名字

使用 if..else 结构,实现提问 “What’s the “official” name of JavaScript?” 的代码。
如果访问者输入了 “ECMAScript”,输出提示 “Right!”,否则输出 “You don’t know? “ECMAScript”!”

解决方案:

let value = prompt('What is the "official" name of JavaScript?', '');

if (value == 'ECMAScript') {
  alert('Right!');
} else {
  alert("You don't know? ECMAScript!");
}

任务:显示符号

编写代码通过 prompt 获取一个数字并用 alert 显示结果:如果数字大于 0,显示 1;小于 0,显示 -1;等于 0,显示 0

解决方案:

let value = prompt('Type a number', 0);

if (value > 0) {
  alert( 1 );
} else if (value < 0) {
  alert( -1 );
} else {
  alert( 0 );
}

任务:使用 '?' 重写 'if' 语句

使用条件运算符 '?' 重写下面的代码:

let result;
if (a + b < 4) {
  result = 'Below';
} else {
  result = 'Over';
}

解决方案:

let result = (a + b < 4) ? 'Below' : 'Over';

任务:使用 '?' 重写 'if..else' 语句

使用多个三元运算符 '?' 重写下面的代码:

let message;

if (login == 'Employee') {
  message = 'Hello';
} else if (login == 'Director') {
  message = 'Greetings';
} else if (login == '') {
  message = 'No login';
} else {
  message = '';
}

解决方案:

let message = (login == 'Employee') ? 'Hello' :
  (login == 'Director') ? 'Greetings' :
  (login == '') ? 'No login' :
  '';

逻辑运算符:灵活的短路魔法

JavaScript 中主要有三种逻辑运算符:||(或),&&(与),!(非)。
与传统语言不同,JavaScript 的逻辑运算符不仅可以处理布尔值,还可以处理任意类型的值,并且它们的返回值也不局限于布尔值。

||(逻辑或 OR)

传统认知: 只有两边都是 false 时才返回 false,只要有一个是 true,结果就是 true。常用于 if 条件中检查是否满足任一条件。

let hour = 12;
let isWeekend = true;

// 如果时间不对 或者 是周末,就关门
if (hour < 10 || hour > 18 || isWeekend) {
  alert( 'The office is closed.' );
}

进阶认知:寻找第一个“真值” (Short-circuit evaluation)
当涉及多个非布尔类型的值时,|| 的内部执行算法如下:

  1. 从左到右依次计算操作数。
  2. 将当前值转化为布尔值。如果是 true立即停止计算,并返回这个操作数的初始原始值(不作布尔转换)。
  3. 如果所有操作数转化后都是 false,则返回最后一个操作数的值。

验证案例:

alert( 1 || 0 );             // 1 (1 已经是真值,立即返回)
alert( null || 1 );          // 1 (null 是假,继续向右找,1 是真值)
alert( undefined || null || 0 ); // 0 (全都是假值,只能返回最后一个)

落地用法 1:获取备用默认值
如果某个变量没有值(即假值),我们可以用 || 提供一个默认数据。

let firstName = "";
let nickName = "SuperCoder";
// firstName 假值被跳过,nickName 真值被选中并返回
alert( firstName || nickName || "Anonymous"); // 输出: SuperCoder

落地用法 2:短路求值控制执行
利用“遇到真值就停止”的特性,可以控制代码是否执行:

// 因为左侧是 true,运算立即停止,右侧的 alert 不会被执行!
true || alert("not printed");

// 左侧是 false,运算必须向右看,所以触发了 alert 的执行
false || alert("printed");

&&(逻辑与 AND)

传统认知: 只有两边都是 true 时才返回 true,只要有一个是 false,结果就是 false

进阶认知:寻找第一个“假值”
|| 的逻辑恰好相反,&& 的执行算法是:

  1. 从左到右计算操作数。
  2. 将当前值转为布尔值。如果是 false立即停止计算,并返回这个操作数的初始原始值
  3. 如果所有操作数转化后都是 true,则返回最后一个操作数的值。

验证案例:

// 如果第一个是假值,立即返回它,后面的全被忽略(短路)
alert( null && 5 );             // null
alert( 0 && "no matter what" ); // 0

// 如果全都是真值,返回最后一个
alert( 1 && 2 && 3 );           // 3

补充说明:优先级问题
与运算 && 的优先级高于或运算 ||
所以表达式 a && b || c && d 会被解析为 (a && b) || (c && d)

避坑提示:不要滥用 && 替代if
有时人们利用 && 遇到假值就短路的特性来缩写 if 语句:
(x > 0) && alert( 'Greater than zero!' );
虽然代码变短了,但语义不清。强烈建议:控制执行流程请用 if,处理逻辑运算请用 &&

!(逻辑非 NOT)

规则: ! 拥有逻辑运算符中最高的优先级。它接收一个参数,将其转为布尔类型后,取反

alert( !true ); // false
alert( !0 );    // true (0转布尔为false,取反为true)

落地用法:双重非 !! 强制转布尔
有时我们使用两个 !! 来将某个值强行转换为布尔类型(效果等同于 Boolean(value))。第一个 ! 负责转换并取反,第二个 ! 再把它反回来。

alert( !!"non-empty string" ); // true

模块小结: 逻辑运算符的强大在于“短路效应”。|| 寻找第一个真值(常用于赋默认值),&& 寻找第一个假值(常用于提前拦截)。理解它们返回的是“原始值”而非“布尔值”,是进阶 JS 的关键一步。


核心任务(练习题)

任务:分析或运算、与运算及短路结果

请写出以下几段代码的输出结果:

  1. alert( null || 2 || undefined );
  2. alert( alert(1) || 2 || alert(3) );
  3. alert( 1 && null && 2 );
  4. alert( alert(1) && alert(2) );
  5. alert( null || 2 && 3 || 4 );

解决方案分析:

  1. 输出2|| 找第一个真值。null 是假,遇到 2 是真,立刻返回 2
  2. 依次输出 1 然后2
    • 首先执行左侧的 alert(1),弹出 1
    • 注意 alert 函数没有显式的 return,默认返回 undefined
    • 此时表达式变成 undefined || 2 || alert(3)
    • 遇到 2 是真值,运算短路停止,最后最外层的 alert 打印出 2alert(3) 不会执行。
  3. 输出null&& 找第一个假值。1 是真,继续向右;遇到 null 是假值,立即返回 null
  4. 依次输出 1 然后undefined
    • 先执行 alert(1) 弹出 1,返回 undefined
    • 此时变成 undefined && alert(2)
    • undefined 是假值,&& 立即停止运算并返回 undefined。外层 alert 打印 undefined
  5. 输出3
    • 优先级 && 高于 ||,先算 2 && 3。两个都是真值,返回最后一个即 3
    • 表达式变为 null || 3 || 4
    • || 寻找第一个真值,即 3,最终打印 3

任务:区间检查

  1. 写一个 if 条件,检查 age 是否位于 1490 的闭区间(包含界限)。
  2. 写一个 if 条件,检查 age 是否不在该闭区间。

解决方案:

// 1. 在区间内
if (age >= 14 && age <= 90)

// 2. 不在区间内 (用 NOT 非运算符)
if (!(age >= 14 && age <= 90))

// 2. 不在区间内 (用 OR 或运算符,更好理解)
if (age < 14 || age > 90)

任务:登录校验流程编写

使用嵌套的 if 块实现一个复杂的校验逻辑。

  1. 使用 prompt 询问 "Who's there?"。
  2. 如果输入 "Admin",则继续用 prompt 询问密码。
    • 密码是 "TheMaster",提示 "Welcome!"
    • 密码为空或取消,提示 "Canceled"
    • 其他密码,提示 "Wrong password"
  3. 如果用户名为空或取消(null),提示 "Canceled"。
  4. 否则提示 "I don't know you"。

解决方案:

let userName = prompt("Who's there?", '');

// 步骤 1:判断用户名
if (userName === 'Admin') {
  
  // 步骤 2:密码环节
  let pass = prompt('Password?', '');

  if (pass === 'TheMaster') {
    alert( 'Welcome!' );
  } else if (pass === '' || pass === null) {
    alert( 'Canceled' );
  } else {
    alert( 'Wrong password' );
  }

} else if (userName === '' || userName === null) {
  alert( 'Canceled' );
} else {
  alert( "I don't know you" );
}

注:注意嵌套 if 结构中的层级缩进,这能极大提高代码的可读性。


【本篇核心逻辑复盘】

  1. 关于类型: 牢记 nullundefined 在不同场景(算术运算 vs 相等比较)下截然不同的表现。防范 "0"true 的布尔陷阱。
  2. 关于运算: + 既是数学加法,又是字符串拼接器,同时还是一元强制转数字神器。清楚算术运算符隐式转数字的铁律。
  3. 关于比较: === 才是你永远的忠实护卫,在无法确定数据类型时,拒绝使用 ==
  4. 关于分支: if 控制流程走向,? 三元运算符负责基于条件返回值。两者职责分明,切勿越界混用。
  5. 关于逻辑: || 找真,&& 找假。它们返回的是引发停机的那个“原始值”,利用这一短路求值特性,我们能写出更加精简强悍的默认值赋值和防御性代码。
贡献者: weew12