# 运算符

back

# Math对象的扩展

🔝🔝

  • 二进制表示法 : 0b或0B开头表示二进制(0bXX或0BXX)
  • 二进制表示法 : 0b或0B开头表示二进制(0bXX或0BXX)
  • 八进制表示法 : 0o或0O开头表示二进制(0oXX或0OXX)
  • Number.EPSILON : 数值最小精度
  • Number.MIN_SAFE_INTEGER : 最小安全数值(-2^53)
  • Number.MAX_SAFE_INTEGER : 最大安全数值(2^53)
  • Number.parseInt() : 返回转换值的整数部分
  • Number.parseFloat() : 返回转换值的浮点数部分
  • Number.isFinite() : 是否为有限数值
  • Number.isNaN() : 是否为NaN
  • Number.isInteger() : 是否为整数
  • Number.isSafeInteger() : 是否在数值安全范围内
  • Math.trunc() : 返回数值整数部分
  • Math.sign() : 返回数值类型(正数1、负数-1、零0)
  • Math.cbrt() : 返回数值立方根
  • Math.clz32() : 返回数值的32位无符号整数形式
  • Math.imul() : 返回两个数值相乘
  • Math.fround() : 返回数值的32位单精度浮点数形式
  • Math.hypot() : 返回所有数值平方和的平方根
  • Math.expm1() : 返回e^n - 1
  • Math.log1p() : 返回1 + n的自然对数(Math.log(1 + n))
  • Math.log10() : 返回以10为底的n的对数
  • Math.log2() : 返回以2为底的n的对数
  • Math.sinh() : 返回n的双曲正弦
  • Math.cosh() : 返回n的双曲余弦
  • Math.tanh() : 返回n的双曲正切
  • Math.asinh() : 返回n的反双曲正弦
  • Math.acosh() : 返回n的反双曲余弦
  • Math.atanh() : 返回n的反双曲正切

# 1 运算符基础

🔝🔝

# 运算符优先级

下面的表将所有运算符按照优先级的不同从高(20)低(1)排列。

优先级 运算类型 关联性 运算符
21 圆括号 n/a(不相关) ( … )
20 成员访问 从左到右 … . …
需计算的成员访问 从左到右 … [ … ]
new (带参数列表) n/a new … ( … )
函数调用 从左到右 … ( … )
可选链(Optional chaining) 从左到右 ?.
19 new (无参数列表) 从右到左 new …
18 后置递增(运算符在后) n/a … ++
18 后置递减(运算符在后) n/a … --
17 逻辑非 从右到左 ! …
17 按位非 从右到左 ~ …
17 一元加法 从右到左 + …
17 一元减法 从右到左 - …
17 前置递增 从右到左 ++ …
17 前置递减 从右到左 -- …
17 typeof 从右到左 typeof …
17 void 从右到左 void …
17 delete 从右到左 delete …
17 await 从右到左 await …
16 从右到左 … ** …
15 乘法 从左到右 … * …
15 除法 从左到右 … / …
15 取模 从左到右 … % …
14 加法 从左到右 … + …
14 减法 从左到右 … - …
13 按位左移 从左到右 … << …
13 按位右移 从左到右 … >> …
13 无符号右移 从左到右 … >>> …
12 小于 从左到右 … < …
12 小于等于 从左到右 … <= …
12 大于 从左到右 … > …
12 大于等于 从左到右 … >= …
12 in 从左到右 … in …
12 instanceof 从左到右 … instanceof …
11 等号 从左到右 … == …
11 非等号 从左到右 … != …
11 全等号 从左到右 … === …
11 非全等号 从左到右 … !== …
10 按位与 从左到右 … & …
9 按位异或 从左到右 … ^ …
8 按位或 从左到右
7 逻辑与 从左到右 … && …
6 逻辑或 从左到右
5 空值合并 从左到右 … ?? …
4 条件运算符 从右到左 … ? … : …
3 赋值 从右到左 … = …
3 赋值 从右到左 … += …
3 赋值 从右到左 … -= …
3 赋值 从右到左 … **= …
3 赋值 从右到左 … *= …
3 赋值 从右到左 … /= …
3 赋值 从右到左 … %= …
3 赋值 从右到左 … <<= …
3 赋值 从右到左 … >>= …
3 赋值 从右到左 … >>>= …
3 赋值 从右到左 … &= …
3 赋值 从右到左 … ^= …
3 赋值 从右到左
3 赋值 从右到左 … &&= …
3 赋值 从右到左
3 赋值 从右到左 … ??= …
2 yield 从右到左 yield …
2 yield* 从右到左 yield* …
1 展开运算符 n/a ... …
0 逗号 从左到右 … , …

# 1.1 优先级: 优先级高的运算符最先被执行

  • 问题:1 || 1 ? 2 : 3 ;
    答案:2

解析:||的优先级高
相当于:(1 || 1 )? 2 : 3
而不是:1 || (1 ? 2 : 3 )

# 1.2 关联性: 运算符执行时的方向。是从左向右,还是从右向左

  • 问题:+function (){var a = b = 1;}();
    console.log(b);
    console.log(a);
    答案:1 error

解析:赋值从右到左,var a = b = 1所以相当于

b = 1;
var a = b;

那有同学可能会问,为什么不是?
var b = 1;
var a = b;

还记得变量提升吗?var a = b = 1;在变量提升的时候,只会把a去声明,并不会执行赋值中的b。 所以要想把b也声明了就需要按照语法 var a=1 , b ;

现在我们仔细把优先级的题改一下

  • 1 || fn() && fn()
    MDN上写的是优先级高的运算符最先被执行,我们都知道 ||是短路的,后边不会执行。那么这个最先被执行的含义是什么呢?

# 1.3 短路

  • && 运算符的短路(a && b):如果a为假,b就不用执行了
  • | | 运算符的短路(a || b):如果a为真,b就不用执行了

但是如果短路运算符的两边都是bool值的话,则是正常的

  • 问题:1 || fn() && fn()
    答案:1 fn不会执行

解析:就是利用&&运算符的短路原理啊。

讲到这有些同学会觉得很简单啊,就是这样啊,看到短路后边就不用算了啊。也有的同学可能会有点懵,不是说好了, 优先级高的先被执行吗?明明&&的优先级高啊。哈哈,别吵吵,我们一起看下一题。

  • 问题:
var a = 42;  
 var b = "foo";  
 var c = 0;  
 c || b ? a : b ; // 42
1
2
3
4

# 2 绑定

定义:运算符的优先级高先执行,并不是真正的执行,而是更强的绑定。

  • 1 || fn() && fn() // &&的优先级高,所以将后边的绑定

  • 1 ||(fn() && fn()) // 所以相当于1 和(fn() && fn())的值去逻辑或

  • 1 ||(fn() && fn()) // 我们查表,逻辑或从左到右执行。

  • 1 ||(fn() && fn()) // 左执行,1是真值,所以短路,后边不执行

  • 问题:

var a = 42;
 var b = "foo";
 var c = 0;
 c || b ? a : b ;
1
2
3
4

答案:42

解析:c || b ? a : b ; //查表 条件运算符权重是4逻辑与符权重是6,所以逻辑与有更强的绑定
(c || b )? a : b ; //(c || b )相当于条件运算符里的条件
(c || b )? a : b ; //(c || b )值是0 ,所以值是 a

好,我们在做两题巩固一下

var a = 5;
 var b = 5;
 var c = 5+a+++b;
1
2
3

[ a , c ] 答案:[6, 15]

解析:b = 5+a+++b; //查表 后置递增权重17 前置递增权重16
b = 5 +(a++)+ b; //++优先级更高,所以和绑定a绑定在一起
b = 5 +(a++)+ b; //根据语法后置递增先执行语句,后递增
b = 5 +(a++)+ b; //执行语句时a是5,所以b是15
b = 5 +(a++)+ b; //a在进行自增,得到6

var a = 5;
 var b = 5;
 var c = ++a-b;
1
2
3

[ a , c ] 答案:[6, 1]

解析:var c = ++a-b; //查表 前置递增权重和一元减权重都是16,从左往右执行
var c = ++a-b; //根据语法前置递增先递增,后执行语句 a = 6
var c = ++a-b; //执行语句时a是6,所以b是1

var a = 42;
 var b = "foo";
 var c = 0;
 a && b || c ? c || b ? a : c && b : a
1
2
3
4

# 3 关联

定义:运算符的关联性去定义表达式的处理方向

  • 问题:a && b && c 的执行顺序
    解析:
    (1)两个运算符都是&&,权重一样。所以这个时候就要看关联性。
    (2)查表 &&的关联性是从左到右
    (3)所以表达式应该是 ( a && b ) && c

  • 问题:a ?b :c ?d : e 的执行顺序
    解析:
    (1)两个运算符都是条件运算符,权重一样。所以这个时候就要看关联性。
    (2)查表条件运算符的关联性是从右到左
    (3)所以表达式应该是 a ?b :(c ?d : e )

# 4 释疑

释疑顾名思义就是解释调疑惑的地方,那最好的办法就是加()。
如果你能够熟练运用优先级/关联的规则,你的代码能更简洁,许多框架都是这样写的,非常漂亮。

# JS 新语法「可选链?」「双问号??」

返回顶部 | 返回:Esnext基础知识

# 案例介绍

back

var street = user.address && user.address.street;
//有了新语法,则可写成:
var street = user.address?.street
1
2
3
var fooInput = myForm.querySelector('input[name=foo]')
var fooValue = fooInput ? fooInput.value : undefined
//有了新语法,则可写成:
var fooValue = myForm.querySelector('input[name=foo]')?.value
1
2
3
4

但在实际使用中,还是会有些不便,比如:

const result = response?.settings?.n || 100
1

你希望如果 response 或者 response.settings 或者 response.settings.n 不存在(值为 null 或者 undefined)时,result 保底值为 100。
但是上面代码在 n 为 0 的时候,也会让 result 变成 100,你实际上希望此时 result 为 0。

于是你只好把代码写成这样

const result = response?.settings?.n === undefined ? 100 : response?.settings?.n
//或者封装一下
const result = fetch(response?.settings?.n, 100)

//现在,你可以用另一个新语法——「双问号语法」——来简化代码:
const result = response?.settings?.n ?? 100;//这个 ?? 的意思是,如果 ?? 左边的值是 null 或者 undefined,那么就返回右边的值。
1
2
3
4
5
6

# 可选链释义

back

Optional Chaining 是 JavaScript 的一个新特性,它允许我们在尝试访问对象的属性之前检查对象是否存在。其他语言也有类似的东西,例如,C# 的 Null Conditional 操作符,与 Optional Chaining 非常类似。
?.

// Still checks for errors and is much more readable.
const nameLength = db?.user?.name?.length;
1
2

如果db、user或name是undefined或null会发生什么?使用 Optinal Chaining 操作符时,JavaScript 会将nameLength初始化为undefined,而不是抛出错误。

# 其他语法形式_调用和动态属性

back

还有一个用来调用可选方法的操作符版本:

// Extends the interface with an optional method, which is present
// only for admin users.
const adminOption = db?.user?.validateAdminAndGetPrefs?.().option;//因为?.()是实际的操作符,它适用于 之前 的表达式
1
2
3

操作符还有第三种用法,就是可选的动态属性访问,通过?.[]实现。它要么返回括号中的参数所引用的值,或者如果没有可以获取值的对象,则返回undefined。

// Extends the capabilities of the static property access
// with a dynamically generated property name.
const optionName = 'optional setting';
const optionLength = db?.user?.preferences?.[optionName].length;
1
2
3
4

需要非undefined默认值时,Optinal Chaining 操作符可以与双问号?? 操作符组合使用。这样可以使用指定的默认值进行安全的深层属性访问

{ // With optional chaining and nullish coalescing:
  const firstName = object?.names?.first ?? '(no first name)';
  // → 'Alice'

  const middleName = object?.names?.middle ?? '(no middle name)';
  // → '(no middle name)'
}
1
2
3
4
5
6
7

# OptinalChaining操作符的属性

back

Optinal Chaining 操作符具有一些有趣的属性:

短路
堆叠
可选删除

短路(Short-circuiting)意味着如果 Optinal Chaining 操作符提前返回,则不对表达式的其余部分求值:

// `age` is incremented only if `db` and `user` are defined.
db?.user?.grow(++age);
1
2

堆叠(Stacking)意味着可以对一系列属性访问应用多个 Optinal Chaining 操作符:

// An optional chain may be followed by another optional chain.
const firstNameLength = db.users?.[42]?.names.first.length;
1
2

可选删除(Optinal deletion)意味着可以将delete操作符与 Optinal Chain 结合使用:

// `db.user` is deleted only if `db` is defined.
delete db?.user;
1
2