js运算符、优先级、逗号运算符

运算符

运算元

运算元 —— 运算符应用的对象。比如说乘法运算 5 * 2,有两个运算元:左运算元 5 和右运算元 2。有时候人们也称其为参数

  • 如果一个运算符对应的只有一个运算元,那么它是 一元运算符。比如说一元运算符 -,它的作用是对数字取反:
let x = 1;

x = -x;
alert( x ); // -1,一元负号运算符生效
  • 如果一个运算符拥有两个运算元,那么它是 二元运算符。减号还存在二元运算符形式:
let x = 1, y = 3;
alert( y - x ); // 2,二元运算符减号做减运算

正式说明:我们正在讨论两种不同的运算符:一元负号运算符(单一运算元,改变正负号)和二元运算符减号(两个运算元,做减法)。

字符串连接功能,二元运算符 +

通常,加号 + 用来求和。

但是如果加号 + 应用于字符串,它将合并(连接)各个字符串:

let s = "my" + "string";
alert(s); // mystring

注意:只要任一运算元是字符串,那么其它运算元也将转化为字符串。

举个例子:

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

可以看出,字符串在前和在后并不影响这个规则。简单来说:如果任一运算元是字符串,那么其它运算元将被转化为字符串。

但是,请注意:运算符的运算方向是由左至右。如果是两个数字,后面再跟一个字符串,那么两个数字会先相加,再转化为字符串:

alert(2 + 2 + '1' ); // "41" 而不是 "221"

字符串连接和转化是加号 + 的一个特性。其它的数学运算符都只对数字有效。通常,他们会转化运算元为数字。

举个例子:减号和除号:

alert( 2 - '1' ); // 1
alert( '6' / '2' ); // 3

数字转化功能,一元运算符 +

加号 + 有两种形式。一种是以上讨论的二元运算符,还有一种是一元运算符。

一元运算符加号,或者说,加号 + 应用于单个值,对数字没有作用。但是如果运算元是非数字,它会将其转化为数字。

比如:

// 对数字无效
let x = 1;
alert( +x ); // 1

let y = -2;
alert( +y ); // -2

// 转化非数字
alert( +true ); // 1
alert( +"" );   // 0

它的效果和 Number(…) 相同,但是更加简短。

拼接字符串,二元运算符 +

二元运算符加号会把他们连接成字符串:

let apples = "2";
let oranges = "3";

alert( apples + oranges ); // "23",二元运算符连接成字符串

如果我们想把它们当做数字对待,我们需要转化它们,然后再求和:

let apples = "2";
let oranges = "3";

// 在二元运算符加号起作用之前,所有的值都转为数字
alert( +apples + +oranges ); // 5
// 更加复杂的写法
// alert( Number(apples) + Number(oranges) ); // 5

为什么一元运算符先于二元运算符作用于运算元?接下去我们将讨论到,这是由于它们拥有更高的 优先级。

运算符优先级

如果一个表达式拥有操作一个运算符,执行的顺序由 优先级 决定。换句话说,在所有运算符中隐含着优先级顺序。

从小学开始,我们知道在表达式 1 + 2 * 2 中,乘法先于加法计算。这就是一个优先级问题。乘法比加法拥有 更高的优先级。

圆括号拥有最高优先级,所以如果我们对现有的运算顺序不满意,我们可以使用圆括号,就像这样:(1 + 2) * 2。

在 JavaScript 中有众多运算符。每个运算符都有对应的优先级数字。数字越大,越先执行。如果优先级相同,那么执行顺序由左至右。

摘抄自 优先级表 (你不必记住它,只要知道一元运算符优先级高于二元运算符):

优先级运算符
16+
16-
14*
14/
13+
13-
3=

赋值运算符

我们知道赋值符号 = 也是一个运算符。在优先级表中显示它的优先级非常低,只有 3。

这也是为什么,当我们赋值时,比如 x = 2 * 2 + 1,所有的计算先执行,然后 = 执行,将计算结果存储到 x。

let x = 2 * 2 + 1;

alert( x ); // 5
链式赋值是可能的:
let a, b, c;

a = b = c = 2 + 2;

alert( a ); // 4
alert( b ); // 4
alert( c ); // 4

链式赋值由右到左执行。首先最右侧表达式 2 + 2 执行,然后将结果赋值给左侧:c、b、a。最后,所有的变量都共享一个值。

赋值运算符 “=” 返回一个值
每个运算符都有一个返回值。对于以加号 + 或者乘号 * 为例的大部分运算符而言,这一点很显然。对于赋值运算符而言,这一点同样适用。

语句 x = value 把 value 的值写入 x 然后返回 x。

下面是一个在复杂语句中使用赋值的例子:

let a = 1;
let b = 2;

let c = 3 - (a = b + 1);

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

以上这个例子,(a = b + 1) 的结果是赋值给 a 的值(即是 3)。然后该值用于与 3 相减。

求余运算符 %

运算符 % 尽管看上去是个百分号,但和百分数没有什么关系。

a % b 的结果是 a 除以 b 的余数。

举个例子:

alert( 5 % 2 ); // 1 是 5 / 2 的余数
alert( 8 % 3 ); // 2 是 8 / 3 的余数
alert( 6 % 3 ); // 0 是 6 / 3 的余数

幂运算符 **

幂运算符 ** 是最近被加入到语法中的。

对于自然数 b,a ** b 的结果是 a 与自己相乘 b 次。

举个例子:

alert( 2 ** 2 ); // 4  (2 * 2)
alert( 2 ** 3 ); // 8  (2 * 2 * 2)
alert( 2 ** 4 ); // 16 (2 * 2 * 2 * 2)

这个运算符对于 a 和 b 是非整数的情况依然适用,举个例子:

alert( 4 ** (1/2) ); // 2 (1 / 2 幂相当于平方,这是数学常识)
alert( 8 ** (1/3) ); // 2 (1 / 3 幂相当于开三次方)

自相加/自相减

对一个数进行加操作或者减操作是最常见的数值运算符。

因此,围绕着它们,有一些特殊的运算符:

  • 自相加 ++ 将变量与1相加:
let counter = 2;
counter++;      // 和 counter = counter + 1 效果一样,但是更加简洁
alert( counter ); // 3
  • 自相减 – 将变量与1相减:
let counter = 2;
counter--;      // 和 counter = counter - 1 效果一样,但是更加简洁
alert( counter ); // 1

重要:
自相加/自相减只能应用于变量。尝试将其应用于数值(比如 5++)会报错。

运算符 ++ 和 – 可以放置在变量前或者变量后。

  • 当运算符在变量后,被称为「后置形式」:counter++。
  • 当运算符在变量前,被称为「前置形式」:++counter。
    两者都做同一件事:将变量 counter 与 1 相加。

那么,有什么区别呢?有,但只有当我们需要 ++/-- 的返回值时才能看到区别。

让我们来明确这一点。我们知道,所有的运算符都有返回值。自相加/自相减也不例外。前置形式返回一个新的值,但后置返回原来的值(做加法/减法之前的值)。

为了直观看到区别,看下面的例子:

let counter = 1;
let a = ++counter; // (*)

alert(a); // 2

(*) 所在的行是前置形式 ++counter,对 counter 做自相加,返回的是新的值 2。因此 alert 显示的是 2。

下面让我们看看后置形式:

 let counter = 1;
let a = counter++; // (*) 将 ++counter 改为 counter++

alert(a); // 1

(*) 所在的行是后置形式 counter++,它同样对 counter 做加法,但是返回的是旧值(做加法之前)。因此 alert 显示的是 1。

总结:

  • 如果自相加/自相减的值不会被使用,那么两者形式没有区别:
let counter = 0;
counter++;
++counter;
alert( counter ); // 2,以上两行作用相同
  • 如果我们想要对变量自相加 并且 立刻使用值,那么我们需要使用前置形式:
let counter = 0;
alert( ++counter ); // 1
  • 如果我们想要使用之前的值,那么我们需要使用后置形式:
let counter = 0;
alert( counter++ ); // 0

自相加/自相减和其它运算符的对比
++/-- 运算符同样可以在表达式内部使用。它们的优先级比绝大部分的运算符要高。

举个例子:

let counter = 1;
alert( 2 * ++counter ); // 4

与下方例子对比:

let counter = 1;
alert( 2 * counter++ ); // 2,因为 counter++ 返回的是「旧值」

尽管从技术层面上来说可行,但是这样的写法会减少代码的可阅读性。在一行上做多个操作 —— 这样并不好。

当阅读代码时,快速的视觉「纵向」扫描会很容易漏掉 counter++,知道那个变量自相加并不简单。

「一行一个操作」模式是更好的选择:

let counter = 1;
alert( 2 * counter );
counter++;

位运算符

位运算符把运算元当做 32 位比特序列,并在它们的二元表现形式上操作。

这些运算符不是 JavaScript 特有的。它们在大部分的编程语言上都得到支持。

下面是位运算符:

按位与 ( & )
按位或 ( | )
按位异或 ( ^ )
按位非 ( ~ )
左移 ( << )
右移 ( >> )
无符号右移 ( >>> )
这些操作使用得非常少。为了理解它们,我们需要探讨底层的数字表达形式,现在不是做这个的最好时机。尤其是我们现在不会立刻使用它。

修改并替换

我们经常需要对一个变量进行操作,然后把新的结果存储给变量。

举个例子:

let n = 2;
n = n + 5;
n = n * 2;

这个操作可以使用运算符 += 和 *= 来简化:

let n = 2;
n += 5; // now n = 7 (同 n = n + 5)
n *= 2; // now n = 14 (同n = n * 2)

alert( n ); // 14

简短的「修改并替换」 运算符对所有的运算符都有效,以及位运算符:/=、-=等等。

这些运算符和正常的赋值运算符拥有相同的优先级,因此它们会在其它运算之后运行:

let n = 2;

n *= 3 + 5;

alert( n ); // 16 (右侧计算首先进行,和 n *= 8 相同)

逗号运算符

逗号运算符 , 是最少见最不常使用的运算符之一。有时候它会被用来写更简短的代码,因此为了能够理解代码,我们需要了解它。

逗号运算符能让我们处理多个语句,使用 , 将它们分开。每个语句都运行了,但是只有最后的语句结果会被返回。

举个例子:

let a = (1 + 2, 3 + 4);

alert( a ); // 7 (3 + 4 的结果)

这里,第一个语句 1 + 2 运行了,但是它的结果被丢弃了,然后 3 + 4 运行,计算结果被返回。

逗号运算符的优先级非常低
请注意逗号运算符的优先级非常低,比 = 还要低,因此上面你的例子中圆括号非常重要。

如果没有圆括号:a = 1 + 2, 3 + 4 会先执行 +,将数值相加得到 a = 3, 7,然后赋值运算符 = 执行, ‘a = 3’,然后逗号之后的数值 7 不会再执行,它被忽略掉了。

为什么我们需要这样一个运算符,它只返回最后一个值呢?

有时候,人们会使用它把几个操作放在一行上来进行复杂的运算。

举个例子:

                            // 一行上有三个运算符
for (a = 1, b = 3, c = a * b; a < 10; a++) {
 ...
}

这样的技巧在许多 JavaScript 框架中都有使用,这也是为什么我们提到它。但是通常它并不能提升代码的可阅读性,使用它之前,我们要想清楚。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值