1 算数运算符
JavaScript总共提供了10个算数运算符,用来完成基本的算术运算:加/减/乘/除法运算符(4个)、余数运算符、指数运算符、自增/减运算符(2个)以及数值运算符、负数值运算符。
1.1 加法运算符
1.1.1 非对象类型值相加
前4种运算符减、乘、除运算符执行的是单纯的数学运算,这里重点是加法运算符,常见用法有:
-
数值相加
-
非数值相加:两个布尔值或数值与布尔值相加,布尔值会自动转换为数值,然后相加。
true+true //2 1+true //2
-
连接字符串:非字符串(如数字或布尔值)和字符串相加,这时非字符串会转为字符串,然后连接在一起。
'a'+'bc' //"abc" 1+'a' //"1a" false+'a' //"falsea"
-
"重载"现象:运算子的不同,导致了不同的语法行为(数值相加或字符串相连),这种现象称为“重载”(overload)。
'3' + 4 + 5 // "345" 3 + 4 + '5' // "75"
注意:除了加法运算符,其他像减法、乘法、除法运算符都不会发生重载现象,它们的规则是:所有运算子一律转为数值,再进行相应的数学运算。
1 - '2' // -1 1 * '2' // 2 1 / '2' // 0.5
1.1.2 对象相加
若运算子为对象,则对象会首先被转为原始类型的值,然后再相加:
var obj={p:1};
obj+2 //"[object Object]2"
实际上,在上述运算中,obj的真实转换为:obj.valueOf().toString();
,其中obj.valueOf();
结果为{p:1}
即返回对象本身,然后调用toString()
方法后变为[object Object]
,即再将其转为字符串。
这样看来:要想得到我们需要的结果,则需要自定定义valueOf()
方法或toString()
方法(Java中称之为重写方法)。我们直接在浏览器控制台进行测试:
-
重写
valueOf()
方法: -
重写
toString()
方法:
特例:若运算子为Date对象,则会优先执行toString()
方法。测试如下:
1.2 余数运算符
-
余数运算的结果取决于第一个运算子的正、负;
-1 % 2 // -1 1 % -2 // 1
-
为了得到负数正确的余数值,可以使用绝对值函数去掉负号。
Math.abs(-1 % 2) //1
-
余数运算符还可以用于浮点数,但无法得到准确的结果:
6.5%2.1 //0.19999999999999973
1.3 自增/减运算符
举个例子:自减运算符同理,这里不再赘述。
++x
:先自增后返回;
x++
:先返回后自增,这种运算之后,变量的值发生变化的情况被叫做运算的副作用(side effect)。
1.4 负/数值运算符
数值运算符的作用:可以将任何值转为数值(与Number
函数的作用相同)。
+true //1
+[] //0
+{} //NaN
-(-1) //1
注意:数值运算符号和负数值运算符,都会返回一个新的值,而不会改变原始变量的值。
1.5 指数运算符
指数运算符为**
,前一个运算子为底数,后一个运算子为指数。
注:指数运算符是右结合运算符,会首先计算第二个指数运算符。
2**4 //16
2**3**2 //2**(3**2)=2**9=512
2 比较运算符
比较运算符返回结果是一个布尔值,表示是否满足指定的条件。除了比较数字,其他类型的值也可以进行比较。
JavaScript总共提供了8个比较运算符,大体分为3类:
- 非相等运算符:大于、小于、大于等于、小于等于运算符;
- 相等运算符与不相等运算符;
- 严格相等运算符与严格不相等运算符;
对于非相等的比较,首先看两个运算子是否都是字符串,若是,则按照字典顺序比较(即比较每个字符的Unicode码);否则,将两个运算子转为数值,再比较数值的大小。
2.1 非相等运算符
-
字符串比较:如下例子,由于
c
的Unicode码为99,C
的Unicode码为67,因此返回值为true
;同样地也适用于汉字,“大”的Unicode码为22823,“小”的Unicode码为23567,显然返回值为false
'cat'>'Cat' //true '大'>'小' //false
-
非字符串比较:字符串和布尔值都会先转成数值,再进行比较。
5>'4' //true,等同于5>Number('4'),即5>4 true>false //true,等同于Number(true)>Number(false),即1>0
任何值(包括
NaN
)与NaN
使用非相等运算符进行比较,返回值都为false
。 -
对象比较:会转为原始类型的值,再进行比较。具体地和上一节加法运算符中对象的转换方法是一致的,即先调用
valueOf()
方法;如果返回的还是对象,再接着调用toString()
方法。
2.2 相等运算符
相等运算符(==
)比较的是两个值是否相等:会将两个值转换为同一种类型的值后,再用===
进行比较;不相等运算符(!=
)则为相等运算符的结果取反。
-
简单值比较:
1==1.0 //true 1==true //true 0==false //true true=='true' //false,等同于Number(true)===Number('true'),即1===NaN ''==0 //true,等同于Number('')===0,即0===0 '1'==true //true,等同于Number('1')===Number(true),即1===1 undefined == undefined // true null == null // true undefined == null // true
-
复合类型的值比较:同样地,对象先调用
valueOf()
方法;如果返回的还是对象,再接着调用toString()
方法。[1]==1 //true [1,2]=='1,2' //true [1]==true //true
2.3 严格相等运算符
严格相等运算符(===
)比较的是它们是否为同一个值:若两个值不是同一类型,则严格相等运算符直接返回false
;严格不相等运算符(!==
)就是对严格相等运算符的结果取反。
-
简单值比较:
1===1.0 //true,等价于1==1.0 1==='1' //false,类型不同 true==='true' //false,类型不同 1===0x1 //true,十进制的1和十六进制的1类型相同,值相同,因此返回true。 +0===-0 //true NaN===NaN //false,NaN与任何值都不相等(包括自身) undefined === undefined // true null === null // true
-
复合类型的值(对象、函数、数组)比较:比较的是它们是否指向同一个地址。
{} === {} // false [] === [] // false (function () {} === function () {}) // false //若两个对象引用同一个对象,则严格相等 var v1 = {}; var v2 = v1; v1 === v2 // true
总结:相等运算符会进行隐式类型转换,建议只使用严格相等运算符。
3 布尔运算符
布尔运算符用于将表达式转为布尔值,共有4个运算符:
-
取反运算符:
!
,像undefined
、null
、false
、0
、NaN
、''
(空字符串)这6个值取反后为true
,其他值则都为false
。注:两次取反就是将一个值转为布尔值的简便写法,等同于
Boolean()
函数。 -
且运算符:
&&
,往往用于多个表达式求值,运算规则为:返回第一个布尔值为false的表达式的值,若所有表达式的值都为true则返回最后一个表达式的值。true && 'foo' && '' && 4 && 'foo' && true //返回值为'' 1 && 2 && 3 //返回值为3
-
或运算符:
||
,也往往用于多个表达式求值,运算规则为:返回第一个布尔值为true的表达式的值,若所有表达式的值都为false,则返回最后一个表达式的值。技巧:或运算符常用于为一个变量设置默认值。
false || 0 || '' || 4 || 'foo' || true //返回值为4 false || 0 || '' // 返回值为''
-
三元运算符:
?:
,与if...else
语句具有同样表达效果。
4 二进制位运算符
二进制位运算符用于对二进制位进行计算,共有7个:
注:位运算只对整数有效,遇到小数时,会将小数部分舍去,只保留整数部分。
- 二进制或运算符,符号为
|
,若两个二进制位都为0
,则结果为0
,否则为1
。 - 二进制与运算符,符号为
&
,若两个二进制位都为1
,则结果为1
,否则为0
。 - 二进制否运算符,符号为
~
,对二进制位取反。 - 异或运算符,符号为
^
,若两个二进制位不同,则结果为1
,否则为0
。 - 左移运算符,符号为
<<
,表示将一个数的二进制值向左移动指定的位数,尾部补0
,即乘以2
的指定次方。向左移动的时候,最高位的符号位是一起移动的。 - 右移运算符,符号为
>>
,表示将一个数的二进制值向右移动指定的位数。如果是正数,头部全部补0
;如果是负数,头部全部补1
,右移运算符基本上相当于除以2
的指定次方,同样地,最高位即符号位也参与移动。 - 头部补零的右移运算符,符号为
>>>
,它与右移运算符(>>
)只有一个差别,就是一个数的二进制形式向右移动时,头部一律补零,而不考虑符号位。所以,该运算总是得到正值。
5 赋值运算符
赋值运算符(Assignment Operators)用于给变量赋值。
赋值运算符最常见的就是=
,但还可以和其他运算符结合使用。
-
和算术运算符结合:
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
)、x^=y
(等同于x=x^y
)。
6 void运算符
void
运算符用于执行一个表达式,然后不返回任何值(或返回undefined
)。
void(0) // undefined
void
运算符的主要用途:浏览器的书签工具,以及在超链接中插入代码防止网页跳转。
模仿添加书签:
<script>
function f() {
console.log('Hello World');
}
</script>
<a href="http://example.com" onclick="f(); return false;">点击</a>
//使用void则写法更加简洁
<a href="javascript: void(f())">文字</a>
提交但并不跳转:
<a href="javascript: void(document.form.submit())">提交</a>
7 逗号运算符
逗号运算符用于对2个表达式求值,并返回后1个表达式的值。
var x = 0;
var y = (x++, 10);
x // 1
y // 10
8 运算顺序
8.1 优先级
JavaScript的各种运算符的优先级别(Operator Precedence)时不一样的,优先级高的高的运算符先执行,优先级低的后执行。
8.2 圆括号
圆括号()
的优先级是最高的,可以用它来提高运算的优先级。建议总是使用圆括号,保证运算顺序清晰可读,这对代码的维护和除错至关重要。
注:圆括号不具有求值的作用,即
(expression)
等同于expression
,它仅改变了运算的优先级。
8.3 左/右结合运算符
在计算时,对于优先级别相同的运算符(例如加和减是同等优先级)同时出现的时候,就会有计算顺序的问题,我们姑且用下面的式子表示(OP
为Operator Precedence的缩写):
a OP b OP c
运算的方式有:
// 方式1
(a OP b) OP c
// 方式2
a OP (b OP c)
若采用方式1运行的运算符,则称为左结合运算符(left-to-right associativity);若采用方式2运行的运算符,则称为右结合运算符(right-to-left associativity)。
JavaScript运算符大多数都为左结合运算符,少数像赋值运算符=
、三元运算符?:
以及指数运算符**
为右结合运算符。
w = x = y = z; //等同于w = (x = (y = z));
q = a ? b : c ? d : e ? f : g; //等同于q = a ? b : (c ? d : (e ? f : g));
2 ** 3 ** 2 //等同于2 ** (3 ** 2)