10种算术运算符
- 加法运算符:
x + y
- 减法运算符:
x - y
- 乘法运算符:
x * y
- 除法运算符:
x / y
- 指数运算符:
x ** y
- 余数运算符:
x % y
- 自增运算符:
++x
或者x++
- 自减运算符:
--x
或者x--
- 数值运算符:
+x
- 负数值运算符:
-x
加法
JavaScript 允许非数值的相加
两个字符串相加,这时加法运算符会变成连接运算符,返回一个新的字符串,将两个原字符串连接在一起。
一个运算子是字符串,另一个运算子是非字符串,这时非字符串会转成字符串,再连接在一起。
加法运算符是在运行时决定,到底是执行相加,还是执行连接。根据不同的运算子导致不一样的语法行为叫重载
'3' + 4 + 5 // "345" 3 + 4 + '5' // "75"
除了加法运算符,其他算术运算符(比如减法、除法和乘法)都不会发生重载。它们的规则是:所有运算子一律转为数值,再进行相应的数学运算。
对象的相加
如果运算子是对象, 必须先转为原始类型的值,再相加, 对象转原始值规则:
先调用对象的
valueOf
, 对象的valueOf
方法总是返回对象自身这时再自动调用对象的
toString
方法,将其转为字符串。var obj = { p: 1 }; obj.valueOf() // { p: 1 } obj.valueOf().toString() // "[object Object]"
知道了这个规则以后,就可以自己定义valueOf
方法或toString
方法,得到想要的结果。
var obj = {
valueOf: function () {
return 1;
}
};
obj + 2 // 3
余数 (%)
运算结果的正负号由第一个运算子的正负号决定。
所以,为了得到负数的正确余数值,可以先使用绝对值函数。
// 错误的写法
function isOdd(n) {
return n % 2 === 1;
}
isOdd(-5) // false
isOdd(-4) // false
// 正确的写法
function isOdd(n) {
return Math.abs(n % 2) === 1;
}
isOdd(-5) // true
isOdd(-4) // false
自增自减
自增和自减运算符,是一元运算符,只需要一个运算子。它们的作用是将运算子首先转为数值,然后加上1或者减去1。它们会修改原始变量。
运算之后,变量的值发生变化,这种效应叫做运算的副作用(side effect)。自增和自减运算符是仅有的两个具有副作用的运算符,其他运算符都不会改变变量的值。
自增和自减运算符有一个需要注意的地方,就是放在变量之后,会先返回变量操作前的值,再进行自增/自减操作;放在变量之前,会先进行自增/自减操作,再返回变量操作后的值。
var x = 1;
var y = 1;
x++ // 1
++y // 2
数值运算符/负值运算符
数值运算符号和负数值运算符,都会返回一个新的值,而不会改变原始变量的值。
数值运算符(+
)同样使用加号,但它是一元运算符(只需要一个操作数),而加法运算符是二元运算符(需要两个操作数)。
数值运算符的作用在于可以将任何值转为数值(与Number
函数的作用相同)
+true // 1
+[] // 0
+{} // NaN
负数值运算符(-
),也同样具有将一个值转为数值的功能,只不过得到的值正负相反。连用两个负数值运算符(需要用括号, 不然是自减运算符),等同于数值运算符。
指数运算符 (**)
指数运算符(**
)完成指数运算,前一个运算子是底数,后一个运算子是指数。
指数运算符是右结合, 即多个指数运算符连用时,先进行最右边的计算.
// 相当于 2 ** (3 ** 2)
2 ** 3 ** 2 // 512
赋值运算符 (=)
下面是与算术运算符的结合。
// 等同于 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
比较运算符
JavaScript 一共提供了8个比较运算符。
>
大于运算符<
小于运算符<=
小于或等于运算符>=
大于或等于运算符==
相等运算符===
严格相等运算符!=
不相等运算符!==
严格不相等运算符
字符串的比较
按照字典顺序进行比较( 实际上是比较Unicode码点)
非字符串的比较
如果运算子都不是字符串
都是原始类型的值, 则先转成数值在比较
5 > '4' // true // 等同于 5 > Number('4') // 即 5 > 4 true > false // true // 等同于 Number(true) > Number(false) // 即 1 > 0 2 > true // true // 等同于 2 > Number(true) // 即 2 > 1
NaN 和 任何值比较都 返回
false
对象
如果运算子是对象,会转为原始类型的值,再进行比较。
对象转换成原始类型的值,算法是先调用valueOf
方法;如果返回的还是对象,再接着调用toString
方法
严格相等运算符 (===)
JavaScript有提供了两种相等运算符: ==
和===
相等运算符(
==
)是比较两个值是否相等,严格相等运算符(===
)比较的是他们是否为’同一个值’, 不是同一类型,===
返回false
,==
会先转为同一类型,在比较
NaN和任何值都不相等(包括自身)
正
0
等于负0
符合类型(对象, 数组, 函数)比较是, 不是比它们值是否相等, 而是比较它们是否是同一个地址
{} === {} // false [] === [] // false (function (){} === function(){}) // false
如果两个变量引用同一个对象,则它们相等。
var v1 = {}; var v2 = v1; v1 === v2 // true
undefined
和null
与自身严格相等由于变量声明后默认值是
undefined
,因此两个只声明未赋值的变量是相等的。
相等运算符 (==)
它遇到相同类型的数据时, 与
===
完全一样, 比较不同类型的数据时,相等运算符会先将数据进行类型转换,然后再用严格相等运算符比较。
下面分成四种情况,讨论不同类型的值互相比较的规则
原始类型的值会转换成数值再进行比较
对象与原始类型值比较 对象转换成原始类型的值,再比较
undefined 和 null 与其他类型数值比较返回
false
,互与自身则返回true
相等运算符的缺点
0 == '' // true 0 == '0' // true 2 == true // false 2 == false // false false == 'false' // false false == '0' // true false == undefined // false false == null // false null == undefined // true ' \t\r\n ' == 0 // true
因此建议不要使用相等运算符, 最好只使用严格相等运算符
布尔运算符
布尔运算符用于将表达式转为布尔值,一共包含四个运算符。
- 取反运算符:
!
- 且运算符:
&&
- 或运算符:
||
- 三元运算符:
?:
取反运算符 (!)
对于非布尔值, 取反会将其 转为布尔值, 以下6个值取反后为 true
, 其他值都为false
:
undefined
null
false
0
NaN
- 空字符串(
''
)
如果对一个值连续做两次取反运算,等于将其转为对应的布尔值,与
Boolean
函数的作用相同。这是一种常用的类型转换的写法。
且运算符 (&&)
规则: 如果一个运算子的布尔值为 true
, 则返回第二个运算子的值(注意是值, 不是布尔值); 如果第一个运算子的布尔值为false
, 则直接返回一第一个子运算符的值, 不再对第二个运算子求值
't' && '' // ""
't' && 'f' // "f"
't' && (1 + 2) // 3
'' && 'f' // ""
'' && '' // ""
var x = 1;
(1 - 1) && ( x += 1) // 0
x // 1
这种只通过第一个表达式的值,控制是否运行第二个表达式的机制,就称为“短路”(short-cut)。有些程序员喜欢用它取代if
结构,比如下面是一段if
结构的代码,就可以用且运算符改写。
if (i) {
doSomething();
}
// 等价于
i && doSomething();
且运算符可以多个连用,这时返回第一个布尔值为false
的表达式的值。如果所有表达式的布尔值都为true
,则返回最后一个表达式的值。
true && 'foo' && '' && 4 && 'foo' && true
// ''
1 && 2 && 3
// 3
或运算符 (||)
规则 : 如果第一个运算子的布尔值为 true
, 则返回第一个运算子的值, 且不再对第二个运算子求值; 如果第一个运算子的布尔值为 false
, 则返回第二个运算子的值;
't' || '' // "t"
't' || 'f' // "t"
'' || 'f' // "f"
'' || '' // ""
短路规则也适用:
var x = 1;
true || (x = 2) // true
x // 1
或运算符可以多个连用,这时返回第一个布尔值为true
的表达式的值。如果所有表达式都为false
,则返回最后一个表达式的值。
false || 0 || '' || 4 || 'foo' || true
// 4
false || 0 || ''
// ''
或运算符常用于为一个变量设置默认值。
function saveText(text) {
text = text || '';
// ...
}
// 或者写成
saveText(this.text || '')
三元运算符 (? :)
三元条件运算符由问号(?)和冒号(:)组成,分隔三个表达式。它是 JavaScript 语言唯一一个需要三个运算子的运算符。
- 通常来说,三元条件表达式与
if...else
语句具有同样表达效果,前者可以表达的,后者也能表达。但是两者具有一个重大差别,if...else
是语句,没有返回值;三元条件表达式是表达式,具有返回值。
console.log(true ? 'T' : 'F');
上面代码中,console.log
方法的参数必须是一个表达式,这时就只能使用三元条件表达式。如果要用if...else
语句,就必须改变整个代码写法了。
二进制运算符 (没看)
其他运算符和运算顺序
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>
逗号运算符: 用于对两个表达式求值, 并返回后一个表达式;
逗号运算符的一个用途是,在返回一个值之前,进行一些辅助操作
'a', 'b' // "b" var x = 0; var y = (x++, 10); x // 1 y // 10
运算优先级
这五个运算符的优先级从高到低依次为:小于等于(<=
)、严格相等(===
)、或(||
)、三元(?:
)、等号(=
)
圆括号的作用
圆括号 ()
用来提高运算的优先级, 它的优先级最高;
圆括号不是运算符,而是一种语法结构。
它一共有两种用法:
- 一种是把表达式放在圆括号之中,提升运算的优先级;
- 另一种是跟在函数的后面,作用是调用函数。