JavaScript 基础知识5
值的比较
我们知道,在数学中有很多用于比较大小的运算符。
在 JavaScript 中,它们的编写方式如下:
- 大于 / 小于:
a > b
,a < b
。 - 大于等于 / 小于等于:
a >= b
,a <= b
。 - 检查两个值的相等:
a == b
,请注意双等号==
表示相等性检查,而单等号a = b
表示赋值。 - 检查两个值不相等:不相等在数学中的符号是
≠
,但在 JavaScript 中写成a != b
。
在本文中,我们将进一步了解不同类型的比较,JavaScript 是如何进行比较的,包括一些重要的特殊性。
在文末给出了一些秘诀,帮助你避免 “JavaScript 陷阱”相关的问题。
比较结果为 Boolean 类型
所有比较运算符均返回布尔值:
true
—— 表示“yes(是)”,“correct(正确)”或“the truth(真)”。false
—— 表示“no(否)”,“wrong(错误)”或“not the truth(非真)”。
示例:
alert( 2 > 1 ); // true(正确)
alert( 2 == 1 ); // false(错误)
alert( 2 != 1 ); // true(正确)
和其他类型的值一样,比较的结果可以被赋值给任意变量:
let result = 5 > 4; // 把比较的结果赋值给 result
alert( result ); // true
字符串比较
在比较字符串的大小时,JavaScript 会使用“字典(dictionary)”或“词典(lexicographical)”顺序进行判定。
换言之,字符串是按字符(母)逐个进行比较的。
例如:
alert( 'Z' > 'A' ); // true
alert( 'Glow' > 'Glee' ); // true
alert( 'Bee' > 'Be' ); // true
字符串的比较算法非常简单:
- 首先比较两个字符串的首位字符大小。
- 如果一方字符较大(或较小),则该字符串大于(或小于)另一个字符串。算法结束。
- 否则,如果两个字符串的首位字符相等,则继续取出两个字符串各自的后一位字符进行比较。
- 重复上述步骤进行比较,直到比较完成某字符串的所有字符为止。
- 如果两个字符串的字符同时用完,那么则判定它们相等,否则未结束(还有未比较的字符)的字符串更大。
在上面的第一个例子中,'Z' > 'A'
比较在算法的第 1 步就得到了结果。
在第二个例子中,字符串 Glow
与 Glee
的比较则需要更多步骤,因为需要逐个字符进行比较:
G
和G
相等。l
和l
相等。o
比e
大,算法停止,第一个字符串大于第二个。
ℹ️非真正的字典顺序,而是 Unicode 编码顺序
在上面的算法中,比较大小的逻辑与字典或电话簿中的排序很像,但也不完全相同。
比如说,字符串比较对字母大小写是敏感的。大写的
"A"
并不等于小写的"a"
。哪一个更大呢?实际上小写的"a"
更大。这是因为在 JavaScript 使用的内部编码表中(Unicode),小写字母的字符索引值更大。我们会在 【字符串】 这章讨论更多关于字符串的细节。
不同类型间的比较
当对不同类型的值进行比较时,JavaScript 会首先将其转化为数字(number)再判定大小。
例如:
alert( '2' > 1 ); // true,字符串 '2' 会被转化为数字 2
alert( '01' == 1 ); // true,字符串 '01' 会被转化为数字 1
对于布尔类型值,true
会被转化为 1
、false
转化为 0
。
例如:
alert( true == 1 ); // true
alert( false == 0 ); // true
ℹ️一个有趣的现象
有时候,以下两种情况会同时发生:
- 若直接比较两个值,其结果是相等的。
- 若把两个值转为布尔值,它们可能得出完全相反的结果,即一个是
true
,一个是false
。例如:
let a = 0; alert( Boolean(a) ); // false let b = "0"; alert( Boolean(b) ); // true alert(a == b); // true!
对于 JavaScript 而言,这种现象其实挺正常的。因为 JavaScript 会把待比较的值转化为数字后再做比较(因此
"0"
变成了0
)。若只是将一个变量转化为Boolean
值,则会使用其他的类型转换规则。
严格相等
普通的相等性检查 ==
存在一个问题,它不能区分出 0
和 false
:
alert( 0 == false ); // true
也同样无法区分空字符串和 false
:
alert( '' == false ); // true
这是因为在比较不同类型的值时,处于相等判断符号 ==
两侧的值会先被转化为数字。空字符串和 false
也是如此,转化后它们都为数字 0。
如果我们需要区分 0
和 false
,该怎么办?
严格相等运算符 ===
在进行比较时不会做任何的类型转换。
换句话说,如果 a
和 b
属于不同的数据类型,那么 a === b
不会做任何的类型转换而立刻返回 false
。
让我们试试:
alert( 0 === false ); // false,因为被比较值的数据类型不同
同样的,与“不相等”符号 !=
类似,“严格不相等”表示为 !==
。
严格相等的运算符虽然写起来稍微长一些,但是它能够很清楚地显示代码意图,降低你犯错的可能性。
对 null 和 undefined 进行比较
当使用 null
或 undefined
与其他值进行比较时,其返回结果常常出乎你的意料。
-
当使用严格相等
===
比较二者时它们不相等,因为它们属于不同的类型。
alert( null === undefined ); // false
-
当使用非严格相等
==
比较二者时JavaScript 存在一个特殊的规则,会判定它们相等。它们俩就像“一对恋人”,仅仅等于对方而不等于其他任何的值(只在非严格相等下成立)。
alert( null == undefined ); // true
-
当使用数学式或其他比较方法
< > <= >=
时:null/undefined
会被转化为数字:null
被转化为0
,undefined
被转化为NaN
。
下面让我们看看,这些规则会带来什么有趣的现象。同时更重要的是,我们需要从中学会如何远离这些特性带来的“陷阱”。
奇怪的结果:null vs 0
通过比较 null
和 0 可得:
alert( null > 0 ); // (1) false
alert( null == 0 ); // (2) false
alert( null >= 0 ); // (3) true
是的,上面的结果完全打破了你对数学的认识。在最后一行代码显示“null
大于等于 0”的情况下,前两行代码中一定会有一个是正确的,然而事实表明它们的结果都是 false。
为什么会出现这种反常结果,这是因为相等性检查 ==
和普通比较符 > < >= <=
的代码逻辑是相互独立的。进行值的比较时,null
会被转化为数字,因此它被转化为了 0
。这就是为什么(3)中 null >= 0
返回值是 true,(1)中 null > 0
返回值是 false。
另一方面,undefined
和 null
在相等性检查 ==
中不会进行任何的类型转换,它们有自己独立的比较规则,所以除了它们之间互等外,不会等于任何其他的值。这就解释了为什么(2)中 null == 0
会返回 false。
特立独行的 undefined
undefined
不应该被与其他值进行比较:
alert( undefined > 0 ); // false (1)
alert( undefined < 0 ); // false (2)
alert( undefined == 0 ); // false (3)
为何它看起来如此厌恶 0?返回值都是 false!
原因如下:
(1)
和(2)
都返回false
是因为undefined
在比较中被转换为了NaN
,而NaN
是一个特殊的数值型值,它与任何值进行比较都会返回false
。(3)
返回false
是因为这是一个相等性检查,而undefined
只与null
相等,不会与其他值相等。
避免问题
我们为何要研究上述示例?我们需要时刻记得这些古怪的规则吗?不,其实不需要。虽然随着代码写得越来越多,我们对这些规则也都会烂熟于胸,但是我们需要更为可靠的方法来避免潜在的问题:
- 除了严格相等
===
外,其他但凡是有undefined/null
参与的比较,我们都需要格外小心。 - 除非你非常清楚自己在做什么,否则永远不要使用
>= > < <=
去比较一个可能为null/undefined
的变量。对于取值可能是null/undefined
的变量,请按需要分别检查它的取值情况。
总结
-
比较运算符始终返回布尔值。
-
字符串的比较,会按照“词典”顺序逐字符地比较大小。
-
当对不同类型的值进行比较时,它们会先被转化为数字(不包括严格相等检查)再进行比较。
-
在非严格相等
==
下,null
和undefined
相等且各自不等于任何其他的值。 -
在使用
>
或<
进行比较时,需要注意变量可能为null/undefined
的情况。比较好的方法是单独检查变量是否等于null/undefined
。
✅任务
值的比较
重要程度: 5
以下表达式的执行结果是?
5 > 4
"apple" > "pineapple"
"2" > "12"
undefined == null
undefined === null
null == "\n0\n"
null === +"\n0\n"
解决方案
5 > 4 → true "apple" > "pineapple" → false "2" > "12" → true undefined == null → true undefined === null → false null == "\n0\n" → false null === +"\n0\n" → false
结果的原因:
- 数字间比较大小,显然得 true。
- 按词典顺序比较,得 false。
"a"
比"p"
小。- 与第 2 题同理,首位字符
"2"
大于"1"
。null
只与undefined
互等。- 严格相等模式下,类型不同得 false。
- 与第 4 题同理,
null
只与undefined
相等。- 不同类型严格不相等。
条件分支:if 和 ‘?’
有时我们需要根据不同条件执行不同的操作。
我们可以使用 if
语句和条件运算符 ?
(也称为“问号”运算符)来实现。
“if” 语句
if(...)
语句计算括号里的条件表达式,如果计算结果是 true
,就会执行对应的代码块。
例如:
let year = prompt('In which year was ECMAScript-2015 specification published?', '');
if (year == 2015) alert( 'You are right!' );
在上面这个例子中,条件是一个简单的相等性检查(year == 2015
),但它还可以更复杂。
如果有多个语句要执行,我们必须将要执行的代码块封装在大括号内:
if (year == 2015) {
alert( "That's correct!" );
alert( "You're so smart!" );
}
建议每次使用 if 语句都用大括号 {}
来包装代码块,即使只有一条语句。这样可以提高代码可读性。
布尔转换
if (…)
语句会计算圆括号内的表达式,并将计算结果转换为布尔型。
让我们回顾一下 【类型转换】 一章中的转换规则:
- 数字
0
、空字符串""
、null
、undefined
和NaN
都会被转换成false
。因为它们被称为“假值(falsy)”值。 - 其他值被转换为
true
,所以它们被称为“真值(truthy)”。
所以,下面这个条件下的代码永远不会执行:
if (0) { // 0 是假值(falsy)
...
}
……但下面的条件 —— 始终有效:
if (1) { // 1 是真值(truthy)
...
}
我们也可以将预先计算的布尔值传入 if
语句,像这样:
let cond = (year == 2015); // 相等运算符的结果是 true 或 false
if (cond) {
...
}
“else” 语句
if
语句有时会包含一个可选的 “else” 块。如果判断条件不成立,就会执行它内部的代码。
例如:
let year = prompt('In which year was ECMAScript-2015 specification published?', '');
if (year == 2015) {
alert( 'You guessed it right!' );
} else {
alert( 'How can you be so wrong?' ); // 2015 以外的任何值
}
多个条件:“else if”
有时我们需要测试一个条件的几个变体。我们可以通过使用 else if
子句实现。
例如:
let year = prompt('In which year was ECMAScript-2015 specification published?', '');
if (year < 2015) {
alert( 'Too early...' );
} else if (year > 2015) {
alert( 'Too late' );
} else {
alert( 'Exactly!' );
}
在上面的代码中,JavaScript 先检查 year < 2015
。如果条件不符合,就会转到下一个条件 year > 2015
。如果这个条件也不符合,则会显示最后一个 alert
。
可以有更多的 else if
块。结尾的 else
是可选的。
条件运算符 ‘?’
有时我们需要根据一个条件去赋值一个变量。
如下所示:
let accessAllowed;
let age = prompt('How old are you?', '');
if (age > 18) {
accessAllowed = true;
} else {
accessAllowed = false;
}
alert(accessAllowed);
所谓的“条件”或“问号”运算符让我们可以更简短地达到目的。
这个运算符通过问号 ?
表示。有时它被称为三元运算符,被称为“三元”是因为该运算符中有三个操作数。实际上它是 JavaScript 中唯一一个有这么多操作数的运算符。
语法:
let result = condition ? value1 : value2;
计算条件结果,如果结果为真,则返回 value1
,否则返回 value2
。
例如:
let accessAllowed = (age > 18) ? true : false;
技术上讲,我们可以省略 age > 18
外面的括号。问号运算符的优先级较低,所以它会在比较运算符 >
后执行。
下面这个示例会执行和前面那个示例相同的操作:
// 比较运算符 "age > 18" 首先执行
//(不需要将其包含在括号中)
let accessAllowed = age > 18 ? true : false;
但括号可以使代码可读性更强,所以我们建议使用它们。
ℹ️请注意:
在上面的例子中,你可以不使用问号运算符,因为比较本身就返回
true/false
:
// 下面代码同样可以实现 let accessAllowed = age > 18;
多个 ‘?’
使用一系列问号 ?
运算符可以返回一个取决于多个条件的值。
例如:
let age = prompt('age?', 18);
let message = (age < 3) ? 'Hi, baby!' :
(age < 18) ? 'Hello!' :
(age < 100) ? 'Greetings!' :
'What an unusual age!';
alert( message );
可能很难一下子看出发生了什么。但经过仔细观察,我们可以看到它只是一个普通的检查序列。
- 第一个问号检查
age < 3
。 - 如果为真 — 返回
'Hi, baby!'
。否则,会继续执行冒号":"
后的表达式,检查age < 18
。 - 如果为真 — 返回
'Hello!'
。否则,会继续执行下一个冒号":"
后的表达式,检查age < 100
。 - 如果为真 — 返回
'Greetings!'
。否则,会继续执行最后一个冒号":"
后面的表达式,返回'What an unusual age!'
。
这是使用 if..else
实现上面的逻辑的写法:
if (age < 3) {
message = 'Hi, baby!';
} else if (age < 18) {
message = 'Hello!';
} else if (age < 100) {
message = 'Greetings!';
} else {
message = 'What an unusual age!';
}
‘?’ 的非常规使用
有时可以使用问号 ?
来代替 if
语句:
let company = prompt('Which company created JavaScript?', '');
(company == 'Netscape') ?
alert('Right!') : alert('Wrong.');
根据条件 company =='Netscape'
,要么执行 ?
后面的第一个表达式并显示对应内容,要么执行第二个表达式并显示对应内容。
在这里我们不是把结果赋值给变量。而是根据条件执行不同的代码。
不建议这样使用问号运算符。
这种写法比 if
语句更短,对一些程序员很有吸引力。但它的可读性差。
下面是使用 if
语句实现相同功能的代码,进行下比较:
let company = prompt('Which company created JavaScript?', '');
if (company == 'Netscape') {
alert('Right!');
} else {
alert('Wrong.');
}
因为我们的眼睛垂直扫描代码。所以,跨越几行的代码块比长而水平的代码更易于理解。
问号 ?
的作用是根据条件返回其中一个值。请正确使用它。当需要执行不同的代码分支时,请使用 if
。
✅任务
if(值为 0 的字符串)
重要程度: 5
alert
弹窗会出来吗?
if ("0") {
alert( 'Hello' );
}
解决方案
是的,它会
任何非空字符串(
"0"
不是空字符串)的逻辑值都是true
。我们可以执行下面的代码来进行验证:
if ("0") { alert( 'Hello' ); }
JavaScript 的名字
重要程度: 2
使用 if..else
结构,实现提问 “JavaScript 的“官方”名称是什么?”的代码
如果访问者输入了 “ECMAScript”,输出就提示 “Right!”,否则 — 输出:“你不知道?ECMAScript!”
解决方案
<!DOCTYPE html> <html> <body> <script> 'use strict'; let value = prompt('What is the "official" name of JavaScript?', ''); if (value == 'ECMAScript') { alert('Right!'); } else { alert("You don't know? ECMAScript!"); } </script> </body> </html>
显示符号
重要程度: 2
使用 if..else
语句,编写代码实现通过 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’ 语句
重要程度: 5
使用条件运算符 '?'
重写下面的 if
语句:
let result;
if (a + b < 4) {
result = 'Below';
} else {
result = 'Over';
}
解决方案
let result = (a + b < 4) ? 'Below' : 'Over';
使用 ‘?’ 重写 ‘if…else’ 语句
重要程度: 5
使用多个三元运算符 '?'
重写下面的 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' : '';
- 点赞
- 收藏
- 关注作者
评论(0)