JavaScript 的运算符
1、算数运算符
1.1 所有算数运算符
- 加法运算符:x + y
- 减法运算符: x - y
- 乘法运算符: x * y
- 除法运算符:x / y
- 指数运算符:x ** y
- 余数运算符:x % y
- 自增运算符:++x 或者 x++
- 自减运算符:–x 或者 x–
- 数值运算符: +x
- 负数值运算符:-x
- 赋值运算符 :=
1.2 加法运算符
1、俩数值相加
2 + 3 // 5
2、布尔值相加
true + true // 2
false + false // 0
1 + true // 2
false + 1 // 1
上面代码中,第一行是两个布尔值相加,第二行是数值与布尔值相加。这两种情况,布尔值都会自动转成数值(true= =1、false==0),然后再相加。
3、两个字符串相加
'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.3 余数运算符
余数运算符(%)返回前一个运算子被后一个运算子除,所得的余数。
12 % 5 // 2
需要注意的是,运算结果的正负号由第一个运算子的正负号决定。
-1 % 2 // -1
1 % -2 // 1
所以,为了得到负数的正确余数值,可以先使用绝对值函数。
// 错误的写法
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.4 自增和自减运算符
自增和自减运算符,是一元运算符,只需要一个运算子。它们的作用是将运算子首先转为数值,然后加上1或者减去1。它们会修改原始变量。
var x = 1;
++x // 2
x // 2
--x // 1
x // 1
上面代码的变量x自增后,返回2,再进行自减,返回1。这两种情况都会使得,原始变量x的值发生改变。
自增和自减运算符有一个需要注意的地方,就是放在变量之后,会先返回变量操作前的值,再进行自增/自减操作;放在变量之前,会先进行自增/自减操作,再返回变量操作后的值。
var x = 1;
var y = 1;
x++ // 1
++y // 2
上面代码中,x是先返回当前值,然后自增,所以得到1;y是先自增,然后返回新的值,所以得到2。
1.5 数值运算符,负数值运算符
数值运算符的作用在于可以将任何值转为数值(与Number函数的作用相同)。
+true // 1
+[] // 0
+{} // NaN
负数值运算符(-),也同样具有将一个值转为数值的功能,只不过得到的值正负相反。连用两个负数值运算符,等同于数值运算符。
var x = 1;
-x // -1
-(-x) // 1
数值运算符号和负数值运算符,都会返回一个新的值,而不会改变原始变量的值。
1.6 指数运算符
指数运算符(**)完成指数运算,前一个运算子是底数,后一个运算子是指数。
2 ** 4 // 16
注意,指数运算符是右结合,而不是左结合。即多个指数运算符连用时,先进行最右边的计算。
2 ** 3 ** 2
// 512
// 相当于 2 ** (3 ** 2)
1.7 赋值运算符
赋值运算符用于给变量赋值。
最常见的赋值运算符,当然就是等号(=)。
// 将 1 赋值给变量 x
var x = 1;
// 将变量 y 的值赋值给变量 x
var 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
2、比较运算符
2.1 概念
比较运算符用于比较两个值的大小,然后返回一个布尔值,表示是否满足指定的条件。
2 > 1 // true
所有比较运算符:
JavaScript 一共提供了8个比较运算符。
- 大于运算符: >
- 小于运算符:<
- 小于或等于运算符: <=
- 大于或等于运算符: >=
- 相等运算符: ==
- 严格相等运算符:===
- 不相等运算符:!=
- 严格不相等运算符: !==
2.2 字符串的比较
'cat' > 'dog' // false
'cat' > 'catalog' // false
JavaScript 引擎内部首先比较首字符的 Unicode 码点。如果相等,再比较第二个字符的 Unicode 码点,以此类推。
'cat' > 'Cat' // true'
上面代码中,小写的c的 Unicode 码点(99)大于大写的C的 Unicode 码点(67),所以返回true。
2.3 非字符串的比较
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的比较。任何值(包括NaN本身)与NaN比较,返回的都是false。
1 > NaN // false
1 <= NaN // false
'1' > NaN // false
'1' <= NaN // false
NaN > NaN // false
NaN <= NaN // false
如果运算子是对象,会转为原始类型的值,再进行比较。
对象转换成原始类型的值,算法是先调用valueOf方法;如果返回的还是对象,再接着调用toString方法。
var x = [2];
x > '11' // true
// 等同于 [2].valueOf().toString() > '11'
// 即 '2' > '11'
x.valueOf = function () { return '1' };
x > '11' // false
// 等同于 [2].valueOf() > '11'
// 即 '1' > '11'
两个对象之间的比较也是如此。
[2] > [1] // true
// 等同于 [2].valueOf().toString() > [1].valueOf().toString()
// 即 '2' > '1'
[2] > [11] // true
// 等同于 [2].valueOf().toString() > [11].valueOf().toString()
// 即 '2' > '11'
{ x: 2 } >= { x: 1 } // true
// 等同于 { x: 2 }.valueOf().toString() >= { x: 1 }.valueOf().toString()
// 即 '[object Object]' >= '[object Object]'
2.4 严格相等运算符
JavaScript 提供两种相等运算符:== 和 ===。
简单说,它们的区别是相等运算符(= =)比较两个值是否相等,严格相等运算符(= = =)比较它们是否为“同一个值”。如果两个值不是同一类型,严格相等运算符(= = =)直接返回false,而相等运算符(==)会将它们转换成同一个类型,再用严格相等运算符进行比较。
- 如果两个值的类型不同,直接返回false。
1 === "1" // false
true === "true" // false
上面代码比较数值的1与字符串的“1”、布尔值的true与字符串"true",因为类型不同,结果都是false。
- 同一类的原始类型值
同一类型的原始类型的值(数值、字符串、布尔值)比较时,值相同就返回true,值不同就返回false。需要注意的是,NaN与任何值都不相等(包括自身)。另外,正0等于负0。
NaN === NaN // false
+0 === -0 // true
- 复合类型值
两个复合类型(对象、数组、函数)的数据比较时,不是比较它们的值是否相等,而是比较它们是否指向同一个地址。
{} === {} // false
[] === [] // false
(function () {} === function () {}) // false
上面代码分别比较两个空对象、两个空数组、两个空函数,结果都是不相等。原因是对于复合类型的值,严格相等运算比较的是,它们是否引用同一个内存地址,而运算符两边的空对象、空数组、空函数的值,都存放在不同的内存地址,结果当然是false。
如果两个变量引用同一个对象,则它们相等。
var v1 = {};
var v2 = v1;
v1 === v2 // true
注意,对于两个对象的比较,严格相等运算符比较的是地址,而大于或小于运算符比较的是值。
var obj1 = {};
var obj2 = {};
obj1 > obj2 // false
obj1 < obj2 // false
obj1 === obj2 // false
上面的三个比较,前两个比较的是值,最后一个比较的是地址,所以都返回false。
- undefined 和 null与自身严格相等。
undefined === undefined // true
null === null // true
由于变量声明后默认值是undefined,因此两个只声明未赋值的变量是相等的。
var v1;
var v2;
v1 === v2 // true
2.5 严格不相等运算符
严格相等运算符有一个对应的“严格不相等运算符”(!==),它的算法就是先求严格相等运算符的结果,然后返回相反值。
1 !== '1' // true
// 等同于
!(1 === '1')
上面代码中,感叹号!是求出后面表达式的相反值。
2.6 相等运算符
相等运算符用来比较相同类型的数据时,与严格相等运算符完全一样。
1 == 1.0
// 等同于
1 === 1.0
比较不同类型的数据时,相等运算符会先将数据进行类型转换,然后再用严格相等运算符比较。下面分成四种情况,讨论不同类型的值互相比较的规则。
- 原始类型值
原始类型的值会转换成数值再进行比较。
1 == true // true
// 等同于 1 === Number(true)
0 == false // true
// 等同于 0 === Number(false)
2 == true // false
// 等同于 2 === Number(true)
2 == false // false
// 等同于 2 === Number(false)
'true' == true // false
// 等同于 Number('true') === Number(true)
// 等同于 NaN === 1
'' == 0 // true
// 等同于 Number('') === 0
// 等同于 0 === 0
'' == false // true
// 等同于 Number('') === Number(false)
// 等同于 0 === 0
'1' == true // true
// 等同于 Number('1') === Number(true)
// 等同于 1 === 1
'\n 123 \t' == 123 // true
// 因为字符串转为数字时,省略前置和后置的空格
上面代码将字符串和布尔值都转为数值,然后再进行比较。
- 对象与原始类型值比较
对象(这里指广义的对象,包括数组和函数)与原始类型的值比较时,对象转换成原始类型的值,再进行比较。
// 对象与数值比较时,对象转为数值
[1] == 1 // true
// 等同于 Number([1]) == 1
// 对象与字符串比较时,对象转为字符串
[1] == '1' // true
// 等同于 String([1]) == '1'
[1, 2] == '1,2' // true
// 等同于 String([1, 2]) == '1,2'
// 对象与布尔值比较时,两边都转为数值
[1] == true // true
// 等同于 Number([1]) == Number(true)
[2] == true // false
// 等同于 Number([2]) == Number(true)
上面代码中,数组[1]与数值进行比较,会先转成数值,再进行比较;与字符串进行比较,会先转成字符串,再进行比较;与布尔值进行比较,对象和布尔值都会先转成数值,再进行比较。
- undefined和null与其他类型的值比较时,结果都为false,它们互相比较时结果为true。
false == null // false
false == undefined // false
0 == null // false
0 == undefined // false
undefined == null // true
2.7 不相等运算符
相等运算符有一个对应的“不相等运算符”(!=),它的算法就是先求相等运算符的结果,然后返回相反值。
1 != '1' // false
// 等同于
!(1 == '1')
3、布尔运算符
3.1 概念
布尔运算符用于将表达式转为布尔值,一共包含四个运算符。
- 取反运算符:!
- 且运算符:&&
- 或运算符:||
- 三元运算符:? :
3.2 取反运算符(!)
取反运算符是一个感叹号,用于将布尔值变为相反值,即true变成false,false变成true。
!true // false
!false // true
对于非布尔值,取反运算符会将其转为布尔值。可以这样记忆,以下六个值取反后为true,其他值都为false。
- undefined
- null
- false
- 0
- NaN
- 空字符串(’’)
!undefined // true
!null // true
!0 // true
!NaN // true
!"" // true
!54 // false
!'hello' // false
![] // false
!{} // false
上面代码中,不管什么类型的值,经过取反运算后,都变成了布尔值。
3.3 且(与)运算符(&&)
它的运算规则是:两个变量参与&&运算时,只有当两个变量均为true时,运算结果才为true,否则结果为false。
3.4 或运算符(||)
它的运算规则是:两个变量参与||运算时,当两个变量有一个为true时,结果即为true,只有当两个变量均为false时结果为false。
3.5 三元条件运算符(?:)
三元条件运算符由问号(?)和冒号(:)组成,分隔三个表达式。它是 JavaScript 语言唯一一个需要三个运算子的运算符。如果第一个表达式的布尔值为true,则返回第二个表达式的值,否则返回第三个表达式的值。
't' ? 'hello' : 'world' // "hello"
0 ? 'hello' : 'world' // "world"
上面代码的t和0的布尔值分别为true和false,所以分别返回第二个和第三个表达式的值。
通常来说,三元条件表达式与if…else语句具有同样表达效果,前者可以表达的,后者也能表达。但是两者具有一个重大差别,if…else是语句,没有返回值;三元条件表达式是表达式,具有返回值。所以,在需要返回值的场合,只能使用三元条件表达式,而不能使用if…else。
上一节:JavaScript 的数组方法大全
下一节: