JavaScript浅析 -- 运算符和优先级
一、运算符
JavaScript中的运算符,主要用于连接简单表达式,组成一个复杂的表达式。在运算过程中,会根据需要将操作数转换成特定的类型进行运算,最后输出指定的类型。我个人习惯将他分为6大类进行记忆:算术运算符,比较运算符,布尔运算符,赋值运算符,位运算符,其他运算符。
(一)算术运算符(10个)
- 加法运算符(Addition):x + y
- 减法运算符(Subtraction): x - y
- 乘法运算符(Multiplication): x * y
- 除法运算符(Division):x / y
- 余数运算符(Remainder):x % y
- 自增运算符(Increment):++x 或者 x++
- 自减运算符(Decrement):--x 或者 x--
- 求负运算符(Negate):-x
- 数值运算符(Convert to number): +x
- 指数运算符 (Exponential operator) : x**y ,如2**4=16
说明:
(1) x++先参与运算然后x再做加一操作。如console.log(2 - a++)
相当于console.log(2-a); a = a+1;
++x先进行加一操作再参与运算。如console.log(2 - ++a)
相当于a=a+1;console.log(2-a)
(2) 取余的正负号取决于第一位数,如-7%2为-1;7%-2为1
,所以为了得到负数的正确余值需要用绝对函数,如判断num是否为偶数时Math.abs(num) % 2
(3) +x一般用来做转数的操作,如+new Date()、+[] // 0、+{} // NaN、+true // 1,其实相当于Number(x)只是结果为正。
(4) +号运算时才确定是相加还是连接,如果有一方是字符串则变成连接符。对于对象相加先调用valueOf()再调用toString(),但是valueOf()返回的一般是原对象,所以一般都会调用toString()。特例,Date对象反着来,先toString()再valueOf()。如'3' + 5 + 6 // 356
和3 + 5 + '6' // ‘86’
。
(二)比较运算符(8个)
- == :相等
- === :严格相等
- != :不相等
- !== :严格不相等
- < :小于
- <= :小于或等于
- > :大于
- >= :大于或等于
说明:
===是严格等于,要求类型和数值都相等(其中NaN===NaN为false);而==会进行隐式转换之后再比较值是否相等。具体的转换规则参考文章。
(三)布尔运算符(4个)
- ! :取反运算符
- && :且运算符
- || :或运算符
- condition? true case : false case :三元条件运算符
说明:
(1) !a会对a先做Boolean()转换操作,然后对转换的布尔值进行取反,如!0为!Boolean(0)结果为true。
(2) 且和或的运算均为短路运算,即a && b
两个为true才为true,a为false则不再管b;a || b
一个为true则为true,a为true则不再管b。
(3) condition ? a : b
相当于if(condition) { a } else { b }
。
(四)位运算符(7个)
- 或运算(or):符号为|,表示两个二进制位中有一个为1,则结果为1,否则为0。
- 与运算(and):符号为&,表示两个二进制位都为1,则结果为1,否则为0。
- 否运算(not):符号为~,表示将一个二进制位变成相反值。
- 异或运算(xor):符号为ˆ,表示两个二进制位中有且仅有一个为1时,结果为1,否则为0。
- 左移运算(left shift):符号为<<
- 右移运算(right shift):符号为>>
- 带符号位的右移运算(zero filled right shift):符号为>>>
(五)赋值运算符(11个)
- x += y // 等同于 x = x + y
- x -= y // 等同于 x = x - y
- x *= y // 等同于 x = x * y
- x /= y // 等同于 x = x / y
- x %= y // 等同于 x = x % y
- x >>= y // 等同于 x = x >> y
- x <<= y // 等同于 x = x << y
- x >>>= y // 等同于 x = x >>> y
- x &= y // 等同于 x = x & y
- x |= y // 等同于 x = x | y
- x ^= y // 等同于 x = x ^ y
(六)其他运算符
- 小括号:有两种用法,如果把表达式放在圆括号之中,作用是求值;如果跟在函数的后面,作用是调用函数。
- void:void运算符的作用是执行一个表达式,然后返回undefined。常见的a标签写上
javascript:void(0)
就是这个原理,会运行冒号后面的表达式但返回undefined就没执行。 - 逗号运算符:逗号运算符用于对两个表达式求值,并返回后一个表达式的值。
- typeof运算符:返回操作数的类型,其中
typeof null
结果'object',typeof Function
结果是'function'。 - instanceof运算符:
a instanceof b
判断a是否是b的实例,返回布尔值。
二、运算符的优先级
运算符的优先级顺序如下(上大于下,左大于右) |
---|
++、--(右结合大于左结合)、-(转负数)、+(转数)、~(位反)、!(取反) |
delete、typeof、void |
*、/、%、+、- |
<=、>=、==、!=、===、!== |
||、&&、?:、赋值、, |
说明:
(1) 优先级指的是同时出现的时候先计算高优先级部分,而不是说先当做这个。例如转数的加号优先级高于加法的加号,1+'3‘++new Date()
不是说把加号先当做转数符,如果是那么1+'3'
结果是4但结果是'13‘,很显然是作为加号使用。而是说先计算+new Date()
得到时间戳1531139346042
,再执行1+'3‘+1531139346042
。
(2) ++、--之类的操作数必须是可以放在赋值左边的数而不能是常数,比如4++
会报错。还有下面的例子也要注意:
var a=0, b=0;
a+++b; // 0
a; // 1
b; // 0
右结合的优先级高于左结合和加号,上面会先找有没有右结合,找到a++
先运算,所以运算顺序是(a++)+b
。
(3) typeof的优先级比加减乘除还高,所以一般习惯把操作数加上括号,如
typeof 2*3; // NaN
typeof (2*3); // 'number'
typeof 2+3; // 'number3'
(4) 逻辑非优先级高于加减乘除,但逻辑或和逻辑与比加减乘除优先级低。!2*0
相当于(!2)*0
。
(5) 逻辑或||、逻辑与&&的短路运算要注意,且返回的是其中的一个表达式,而不是布尔值。1 && "hi" || 0
的结果是'hi'
。
(6) 赋值的优先级非常低。
a = b == c // 相当于a = (b==c)
下面这个例子,先自己猜一下结果,再看正确答案。arr.length会报错吗?
var arr, arr2;
arr2 = arr = [1, 2, 3];
arr[arr.length] = arr = 4;
console.log(arr);
console.log(arr2);
上面这个,由于赋值的优先级非常低最后才执行,arr[arr.length] = arr = 4
其实是先算的arr[arr.length]
是指向arr[3]
的一个地址(和arr2[3]
指向的是同一个),接着4赋值给arr然后arr的值4赋值给这块地址指向的内存(也就是arr2[3]
变成了4)。所以结果是arr为4,arr2为[1, 2, 3, 4]。
再来看个例子也是类似。
var a = {n: 1};
var b = a;
a.x = a = {n:2};
console.log(a.x); // undefined
console.log(b.x); // {n: 2}