javaScript除了有数学中一些运算,还有逻辑运算、赋值运算等,参与运算等可以是操作数、也可以是复杂的表达式,两个不同类型的操作数进行操作的时候往往还会进行类型的转换,因此javaScript的运算规则也相对复杂,包含了运算符的优先级、表达式的计算、类型转换等。
一、类型转换
在介绍运算符之前,先介绍一下javaScript的类型转换规则。在javaScript中,存在着两种类型:原始类型和对象类型。
原始类型包括:Undefined、Null、Boolean、Number、String、Symbol、BigInt, 对象类型包括:Object、Function、Array、Date、RegExp、Map、Set、WeakMap、WeakSet等。
javaScript的类型转换规则如下:
- 原始类型之间的转换:
- 字符串到数字:Number(string)
- 数字到字符串:String(number)
- 布尔值到数字:Number(boolean)
- 数字到布尔值:Boolean(number)
- 字符串到布尔值:Boolean(string)
- 布尔值到字符串:String(boolean)
- 对象类型之间的转换:
- 原始类型到对象类型:Object(primitive)
- 对象类型到原始类型:Primitive(object)
上面这些通过Number、String、Boolean等函数进行类型转换,称为“强制类型转换”,也叫显式类型转换。与之对应的是隐式类型转换,即在运算过程中,当运算符左右两边的类型不同时,javaScript会自动进行类型转换。
二、加法运算(+)
运算规则:
- 有一个操作数是字符串类型的进行字符串拼接,将不是字符串类型的转换为字符串类型
- 其中一个操作数是数字类型的,进行数字相加运算,将另一个不是数字的类型的转为数字类型
- 对象类型的先将对象转换为原始类型,再进行上述操作,转换规则是先调用valueOf()方法,如果返回原始类型,则进行上述操作,否则调用toString()方法,再进行上述操作。
1+1 // 2
1+'1' // '11'
1+undefined // NaN
1+null // 1
1+true // 2
1+[] // '1'
1+{} // '[object Object]'
1+function(){} //'1function(){}'
1+NaN // NaN
1+ Symbol() // TypeError: Cannot convert a Symbol value to a number
1+ 1111n // can not mix BigInt and other types. bigint只能和bigin进行运算
undefined + null // NaN unexpected 和任意值数字类型相加都是NaN, 因为Number(undefined)=NaN
null+false // 0
"1" + 1 // "11"
"1" + true // "1true"
"1" + [] // "1"
"1" + {} // "1[object Object]"
"1" + function(){} // "1function(){}"
"1"+null // "1null"
"1"+undefined // "1undefined"
{}+[] // 0 {}被当作代码块
({}+[]) // [object Object]
三、减法运算(-)
减法运算就是数学中的减法运算:
- 先将两个操作数转换数字类型进行计算,注意NaN也是数字类型,和任何数字进行运算都会返回NaN
- 对象类型的先将对象转换为原始类型,再进行上述操作,转换规则是先调用valueOf()方法,如果返回原始类型,则进行上述操作,否则调用toString()方法,再进行上述操作。
1-1 // 0
1-'1' // 0
1-undefined // NaN
1-null // 1
1-true // 0
1-[] //1
1-{} //NaN
1-function(){} //NaN
1-NaN // NaN
1- Symbol() // TypeError: Cannot convert a Symbol value to a number
1- 1111n // can not mix BigInt and other types. bigint只能和bigin进行运算
undefined - null // NaN unexpected 和任意值数字类型相加都是NaN, 因为Number(undefined)=NaN
null-false // 0
[]-{} // NaN
undefined-[] // NaN
true-[] // 1
*(乘法运算)、/(除运算)、%(取模运算)、**(幂运算)当运算规则与减法相同,这里就不再赘述。
3 * 2 = 6;
3 / 2 = 1.5;
3 % 2 = 1;
3 ** 2 = 9;
四、赋值运算(=)
基础类型是值复制,对象类型是引用赋值。
let a = 1;
let b = a;
a = 2;
console.log(b); // 1
let obj1 = {name: 'obj1'};
let obj2 = obj1;
obj1.name = 'obj2';
console.log(obj2.name); // obj2
复合赋值运算 复合赋值运算符包括:+=、-=、*=、/=、%=、**=等,运算的时候先对右边的操作数进行=左边的符号运算,再赋值给左边的变量。
let a = 1;
a += 2;
console.log(a); // 3
let b = 2;
b **= 3;
console.log(b); // 8
五、 逻辑运算符 &&(与)、||(或)、!(非)
- && 运算符只有两个操作数都为true,才返回true,否则返回false。
- || 运算符只要有一个操作数为true,就返回true,否则返回false。
- ! 运算符对操作数进行逻辑非运算,如果操作数为true,则返回false,否则返回true。
- 在进行逻辑运算的时候如非布尔类型的操作数,会先调用Boolean()函数转换为布尔类型,再进行运算,undefined、null、0、NaN、''都为false,其他值都为true。
true && true // true
true && false // false
Boolean([]) // true
Boolean({}) // true
逻辑或和逻辑非还有一种短路求值的用法:
- 逻辑与运算符 && 在第一个操作数为false,不会再进行第二个操作数的运算。
- 逻辑或运算符 || 在第一个操作数为true,不会再进行第二个操作数的运算。
let a = false && 1; // false
let b = 1 || 2; // 1
六、条件运算符(?)
条件运算符,也叫三元运算符,是一种特殊的运算符,有三个操作数,前两个为条件表达式,第三个为表达式,当条件表达式为true时,返回第二个表达式的值,否则返回第三个表达式的值。
let a = 1 > 2? 1 : 2; // 2
let b = 1 < 2? 1 : 2; // 1
七、空值合并运算符(??)
?? 空值合并运算符,是一种特殊的运算符,有两个操作数,第一个操作数为表达式,第二个操作数为默认值,如果第一个操作数为null、undefined则返回第二个操作数的值,否则返回第一个操作数的值。 空值合并运算符用来解决变量为0、’‘时的条件赋值问题。
const count = 0;
const string = '';
let a = count || 1; // 1
let b = string || 'default'; // 'default'
let c = count?? 1; // 0
let d = string?? 'default'; // ''
八、比较运算符
比较运算符包括:
- == 宽松相等
- === 严格相等
- != 不相等 !== 严格不相等
- > 大于
- < 小于
- >= 大于等于
- <= 小于等于
宽松相等(==)在进行比较的时候,会先转换成相同类型的数据再进行比较。
1 == '1' // true
1 == true // true
对象类型的数据不管是宽松相等还是严格相等,都是比较引用地址是否相同。
let obj1 = {name: 'obj1'};
let obj2 = obj1;
obj1 == obj2 // true
obj1 === obj2 // true
object1 == {name: 'obj1'} // false
基础数据类型和对象类型数据进行比较的时候会先把复杂数据类型转换为字符串或者数字再进行比较.
[] == 0 // true
{} == '[object Object]' // true
===严格相等在比较多时候不会进行类型的转换,类型不同直接返回false。
1 === '1' // false
1 === true // false
有一些特殊的情况 undefined和null宽松相等,但是他们和任何类型都不相等, NaN和任何类型包括自身都不相等。
undefined == null // true
undefined == NaN // false
null == 0; // false
null == false
NaN == NaN // false
!=和!==的运算规则和!=、!==相同,区别是!=和!==对结果取反,不再赘述。
>=、<=、>、<会对操作数进行大小比较,js中的数字和字符串类型都可以进行大小比较,数字是进行数值大小的比较,字符串类型是按照ASCII码的位置进行比较。其它的类型会先转换成数字或字符串再进行比较。
1 >= 2 // false
'a' > 'A' // true a的ASCII码为97大于A的ASCII码65
九、逗号运算符
逗号运算符允许在一个表达式中包含多个表达式用逗号隔开,从左往右依次计算每个表达式,并返回最后一个表达式的结果。
const a = (1,2,3); // 3
逗号运算符可以用来简化复制操作
let a = 1;
let b = (a++, a+2);
console.log(b); // 4
一个比较常见的场景就是for循环
for(let i = 0, j = 1; i < 10; i++, j*=2) {
console.log(i, j);