JavaScript is weird! 记一些奇奇怪怪的表达式打牢基础

首先在 https://jsisweird.com/ 这个网站看看究竟能对多少题。

记录一下我所不清楚的地方。

数字、无穷与0相互运算会发生什么?

2 / 0 // Infinity
0 / 0 // NaN
-1 / 0 //-Infinity
Infinity * 0 // NaN

原始类型(基本类型)

基本类型(基本数值、基本数据类型)是一种既非对象也无方法的数据。在 JavaScript 中,共有7种基本类型:string,number,bigint,boolean,null,undefined,symbol (ECMAScript 2016新增)。

注意:
虽然 typeof null === 'object' ,但 null 也是原始类型。
typeof NaN === 'number',NaN 是原始类型。

The ECMAScript Language Specification explains NaN as a number value that is a IEEE 754 “Not-a-Number” value. It might seem strange, but this is a common computer science principle.

There are some odd issues surrounding NaN in JavaScript, however. For instance, this is one of the very few instances where the Object.is function disagrees with triple equal.

NaN === NaN; // -> false
Object.is(NaN, NaN); // -> true

Another such rare instance can be seen in question 24.

This legacy issue was later remedied with the isNaN function.

isNaN(NaN); // -> true

加减运算符发生的强制类型转换到底是返回什么?

是返回对两边的 toString() ? 又或者是 Number() ?

看下 ECMAScript Language Specification 这段话:

总结一下。

对于+ 运算符

  1. 当一边为 string,被识别为字符串拼接,会优先将另一边转换为 string 类型进行拼接。
  2. 当一边为引用类型,则两边都转化成字符串进行拼接。
  3. 当一边为 number,另一边为原始类型,则另一边转换为 number 。
  4. 两边都为非 number 且 非 string 的原始类型,先尝试将一边强制转化为 number 。
123 + '123' // 123123   (规则1)
123 + null  // 123    (规则3)
123 + true // 124    (规则3)
123 + {}  // 123[object Object]    (规则2)

对于 - ,* ,/ 运算符

在对各种非 number 类型运用数学运算符(- * /)时,会先将非 number 类型转换为 number。

1 - true // 0, 首先把 true 转换为数字 1, 然后执行 1 - 1
1 - null // 1,  首先把 null 转换为数字 0, 然后执行 1 - 0
1 * undefined //  NaN, undefined 转换为数字是 NaN
2 * ['5'] //  10, ['5']首先会变成 '5' -- 拆箱操作, 然后再变成数字 5

truthy 值

看下这个

In JavaScript, a truthy value is a value that is considered true when encountered in a Boolean context. All values are truthy unless they are defined as falsy. That is, all values are truthy except false, 0, -0, 0n, "", null, undefined, and NaN.

下面这些都是 truthy 值:

if (true)
if ({})
if ([])
if (42)
if ("0")
if ("false")
if (new Date())
if (-42)
if (12n)
if (3.14)
if (-3.14)
if (Infinity)
if (-Infinity)

如果操作符左边的运算对象是 truthy 值,则 && 操作符返回操作符右边的运算对象:

true && "dog"
// returns "dog"

[] && "dog"
// returns "dog"

== 操作符的规则

看下 ecma262 第11版中关于抽象等式比较(Abstract Equality Comparison)的介绍

我写了下了每一条规则对应的场景:

/* 类型相同,的执行全等运算判断 **/
1 == 2 // false
1 == 1 // true

/* 有一边是 undefined 且另一边是 null, 返回 true **/
undefined == null // true
null == undefined // true

/* 一边是string 一边是 number,先将 string 转为 number **/
1 == '1' // true
1 == '1a' // false, '1a' 调用 Number() 会转化为 NaN

/* 一边是string 一边是 bigint,先将 string 转为 bigint **/
1n == '1' // true
1n == '1a' // false, '1a' 调用 Number() 会转化为 NaN

/* 只要一边是boolean,先将 boolean 强制转为 number **/
1 == true // true
false == 1 // 

/* 一边是 string, number, symbol 或 bigint,另一边是 Object 引用类型,先将 Object 转为原始值 **/
'1,2,[object Object]' == ['1', 2, {}] // true

/* 一边是  bigint, 另一边是 number **/
/* 如果有一边是 NaN,+Infinity, -Infinity,返回 false,否则比较数学上的值的大小 **/
+Infinity == 999999999999999999999999999n // false
1 == 1n // true

发现:

  1. number 除了和引用类型比较外,和其他类型比较时都是其他类型先转为 number。
  2. NaN 和其他任何类型比较永远返回 false(包括和它自己)。
  3. 除了 undefined == null 外,这两个值和其他任何类型比较都为 false
  4. 只要一边是boolean,先将 boolean 强制转为 number
  5. boolean 外的原始类型和引用类型比较,先把引用类型转为原始类型。

二进制,八进制,十进制,十六进制和它们的相互运算

参考:MDN Web Docs Numbers and dates

二进制(binary)

0b0B 开头,如:

0b01111111100000000000000000000000; // 2139095040

八进制(octal)

  1. 如果前导0后面的数字在 070 \sim 7 范围内,则被解读为八进制;
const n = 0755; // 493
0888 // 888 parsed as decimal
const a = 0o10; // ES2015: 8
const b = 00777; // 510,即 8^0 7 + 8^1 * 7 + 8^2 * 7
  1. 0o开头。

十六进制(Hexadecimal)

0x0X 开头。

以 10 为底的指数

nnemmnnEmm 表示 n×10mn \times 10^m

1E3   // 1000
2e6   // 2000000
0.1e2 // 10

相互运算

都先转为十进制再运算。

逻辑 && 操作符

The logical AND operator is usually used with Boolean values in if-statements, but it actually returns the value of one of the operands. If the first expression can be converted to true, then it returns the second. Otherwise, it returns the first.

只要第一个表达式为 false,就返回第一个表达式的值;如果第一个为 true,则返回第二个表达式的值。

"" && -0; // -> ""
-0 && ""; // -> -0
5 && 3; // -> 3
0 && 3; // -> 0

练习题

1.

[] == ![]

右边先转化为布尔值。由于 [] 是 truthy 值,所以 ![] 是 false。

[] == false,一边为引用类型,一边为布尔类型。只要有一边是布尔,就先将布尔类型转为 number。

[] == 0, 一边为 number 类型(非 boolean 的原始类型),一边为引用类型。则先将引用类型转为原始类型。

'' == 0。一边为 string 类型,一边为 number 类型。则先将 string 转为 number。

0 == 0。则最后为 true

2.

true == "true" 

1 == "true" -> 1 == NaN,则最后为 false

3.

010 - 03
010; // -> 8
03; // -> 3
8 - 3; // -> 5

所以答案为5。

4.

1/0 > 10 ** 1000

JavaScript treats both of these values as infinite, and infinity is equal to infinity. Learn more about infinities.

1/0; // -> Infinity
10 ** 1000; // -> Infinity
Infinity > Infinity; // -> false

所以答案为 false。

5.

true++

输出 stytaxError。

只能死记下面的值了:

true++; // -> SyntaxError
1++; // -> SyntaxError
"x"++; // -> SyntaxError
null++; // -> SyntaxError
undefined++; // -> NaN
$++; // -> NaN
console.log++; // -> NaN
NaN++; // -> NaN

let _true = true;
_true++;
_true; // -> 2

6.

undefined + false

While false can be converted to a number, undefined cannot.
Number(undefined); // -> NaN
Number(false); // -> 0
NaN + 0; // -> NaN

7.

"" && -0

考察&&运算符的逻辑。
第一个表达式为 false,直接返回第一个表达式的值。
第一个表达式为 true,则返回第二个表达式的值。

此题第一个表达式为 false ,则返回第一个,即 ""

参考

JavaScript 隐式类型转换,一篇就够了!