前端的运算符有哪些,前端的运算的优先级 实际应用的例子(了解即可,不做深入学习)
1. 汇总表格
首先看下MDN(developer.mozilla.org/zh-CN/docs/…
2. 运算符
JavaScript中的运算符,主要用于连接简单表达式,组成一个复杂的表达式。在运算过程中,会根据需要将操作数转换成特定的类型进行运算,最后输出指定的类型。习惯将他分为6大类进行记忆:算术运算符,比较运算符,布尔运算符,位运算符,赋值运算符,其他运算符。
2.1 算术运算符 (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) : xy ,如24=16
说明:
- 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)
- 取余的正负号取决于第一位数,如-7%2为-1;7%-2为1,所以为了得到负数的正确余值需要用绝对函数,如判断num是否为偶数时Math.abs(num) % 2
- +x一般用来做转数的操作,如+new Date()、+[] // 0、+{} // NaN、+true // 1,其实相当于Number(x)。
- +号运算时才确定是相加还是连接,如果有一方是字符串则变成连接符,否则是转成数字相加。对于对象相加先调用valueOf()再调用toString(),但是valueOf()返回的一般是原对象,所以一般都会调用toString()。 特例,Date对象反着来,先toString()再valueOf()。如'3' + 5 + 6 // 356和3 + 5 + '6' // '86'。
- 0除以0会得到NaN;而非0数值除以0,会返回Infinity,若带负号则结果为-Infinity。
0 / 0 // NaN
1 / 0 // Infinity
1 / -0 // -Infinity
-1 / 0 // -Infinity
-1 / -0 // Infinity
2.2 比较运算符(8个)
== :相等 === :严格相等 != :不相等 !== :严格不相等 < :小于 <= :小于或等于
:大于 = :大于或等于
说明: ===是严格等于,要求类型和数值都相等(其中NaN===NaN为false);而==会进行隐式转换之后再比较值是否相等。具体的转换规则参考文章()
2.3 布尔运算符(4个)
! :取反运算符 && :且运算符 || :或运算符 condition? true case : false case :三元条件运算符
说明:
!a会对a先做Boolean()转换操作,然后对转换的布尔值进行取反,如!0为!Boolean(0)结果为true。 且和或的运算均为短路运算,即a && b两个为true才为true,a为false则不再管b;a || b一个为true则为true,a为true则不再管b。 condition ? a : b相当于if(condition) { a } else { b }。
2.4 位运算符(7个)
或运算(or):符号为|,表示两个二进制位中有一个为1,则结果为1,否则为0。 与运算(and):符号为&,表示两个二进制位都为1,则结果为1,否则为0。 否运算(not):符号为~,表示将一个二进制位变成相反值。 异或运算(xor):符号为ˆ,表示两个二进制位中有且仅有一个为1时,结果为1,否则为0。 左移运算(left shift):符号为<< 右移运算(right shift):符号为>> 带符号位的右移运算(zero filled right shift):符号为>>>
2.5 赋值运算符(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
2.6 其他
- 小括号:有两种用法,如果把表达式放在圆括号之中,作用是求值;如果跟在函数的后面,作用是调用函数。
- void:void运算符的作用是执行一个表达式,然后返回undefined。常见的a标签写上javascript:void(0)就是这个原理,会运行冒号后面的表达式但返回undefined就没执行。
- 逗号运算符:逗号运算符用于对两个表达式求值,并返回后一个表达式的值。
- typeof运算符:返回操作数的类型,其中typeof null结果'object',typeof Function结果是'function'。
- instanceof运算符:a instanceof b判断a是否是b的实例,返回布尔值。
3. 扩展元算符 (...)的优先级
原因在于虽然我们平时叫它扩展运算符,但它其实是一种语法,只能在函数/数组/对象中使用,得到的结果是一种特殊的迭代器
我对其进行了测试,发现它的优先级低于所有运算符
let a = '123'
console.log(...a && '321') // 3 2 1
console.log(...a = '321') // 3 2 1
console.log(...a, '321') // 1 2 3 321
// 最后一条语句逗号是作为操作符使用的,由于 ... 使用场景的限制,无法与逗号运算符进行比较
4. 逗号运算符
逗号作为运算符时,其优先级是最低的。
有逗号分割的表达式,不管前面的结果再花哨。都以最后一段表达式的结果返回
for (let i = 0; false,0,true, false; i++) {
console.log(i) // 一次也不会执行
}
注意逗号作为运算符和操作符的区别
逗号只有在表达式中作运算符使用,在其他地方都是作为操作符使用的,如:函数传参,变量声明、数组/对象定义
console.log(1, 2, 3, 4, 5, 6) // 1 2 3 4 5 6
console.log((1, 2, 3, 4, 5, 6)) // 6
console.log([1, 2, 3]) // [1, 2, 3]
console.log([(1, 2, 3)]) // [3]
5. 优先级
优先级指的是同时出现的时候先计算高优先级部分,就是同样一个表达式,先算那个,后算那个
简单的例子就是加减乘除,先乘除后加减,对于这个表达式 2 + 2 * 2,相当于 2 + (2 * 2),结果为 6 我们只要在计算表达式时,按照优先级给表达式加括号,运算顺序就清晰了 由于逻辑与比逻辑或的优先级高,false && true || true 等同于 (false && true) || true,这样看起来是不是一眼就能出结果了
6. 结合性
结合性只针对二元表达式,决定连续的同级运算符之间,要如何结合
不太好理解,好在正儿八经的右结合性的只有幂
举个例子大家就明白了
2 * 2 * 3 等同于 (2 * 2) * 3 结果为 12 而 2 ** 2 ** 3 等同于 2 ** (2 ** 3) 结果为 256
- new 和条件运算符并不是二元的,结合性对他们是不生效的
另一个右结合性的二元运算符是赋值,我们基本不会连续使用,因为它要求左值为变量,但还是举个例子讲一下吧,看下面这段代码:
let a = 1
a += a *= a -= 1
// 根据右结合原理,上面的表达式化为 a += (a *= (a -= 1))
// 展开等号, a = a + (a = a * (a = a - 1))
// 忽略多余的赋值运算,变量赋值, a = 1 + (1 * (1 - 1))
// 最后表达式结果为 1,a 的值也还是 1
7. 逻辑短路
拥有逻辑短路的有三个运算符:逻辑与、逻辑或、条件运算符
let num = 1
const add = () => num++
false && add()
true || add()
false ? add() : 0
true ? 0 : add()
console.log(num) // 1
在上面这个例子中,按理来说函数调用的优先级远高于逻辑/条件运算符,但是由于逻辑短路, add 一次都没有执行。
逻辑短路没有原因,是一种语言特性,你可以将其理解为懒求值:只有真正用到的时候,才进行计算表达式的值。 而上面的几句表达式的结果在函数执行前就已经确定了,函数也就没有必要执行了
- 但是要注意计算是从左到右计算的,如果函数放在前边就会执行
let num =0
const add = () => { num++; console.log(num)}
add() && false // add 执行
add() || true // add 执行
// 1 2 true
8. 前置/后置运算符
一句话说明白,以递增运算符举例,前置递增先加后运算,后置递增先运算再加
let a=1, b=1
console.log(a++) // 1
console.log(++b) // 2
常见题+练习题
// 第一题
let a = 1
console.log(a-- + --a)
console.log(a)
// 第二题
console.log(!2 - 1 || 0 - 2)
// 第三题
console.log(0 < 1 < 2 == 2 > 1 > 0)
// 第四题
console.log(...typeof 1)
// 第五题
var top = 1['toString'].length
var fun = new new Function()()
console.log((1 > 2 ? 1 : 2 - 10 ? 4 : 2, 1 ? top : fun))
第一题:
递减运算优先于加法运算,加括号:(a--) + (--a) 后置递减先运算再减,所以加号左边为 1,此时 a 为 0 前置加法先减后运算,所以加号右边为 -1,此时 a 为 -1 运算结果为 1 + -1 = 0,a 最终为 -1
第二题:
逻辑非优先于减法优先于逻辑或,加括号:((!2) - 1) || (0 - 2) !2 转化为布尔为 false,再转化为数字为 0,式子为:(0 - 1) || (0 - 2) 逻辑或前一项为 -1,为真值,触发逻辑短路,运算结果为 -1
所以这里可以有变种题,关于触发逻辑短路
let b = 1
console.log(!2 - 1 || b-- )
console.log(b) // b 不执行
第三题:
小于/大于优先于等于,加括号:((0 < 1) < 2) == ((2 > 1) > 0) 左边 true < 2,true 转化为数字 1 < 2,左边结果为 true 右边 true > 0,true 转化为数字 1 > 0,右边结果也为 true 所以,运算结果为 true
第四题: typeof 1 结果为 'number' 展开输出结果为 n u m b e r
第五题: 注意表达式中间有个逗号,所以前半部分不用管,只看后半部分 1 ? top : fun,1 为真值,所以结果为 Window
// 第一题
let flag = false
console.log(typeof flag) // boolean
console.log(typeof flag ? '2' : 1) // 2
// typeof的计算运算级比三元运算符高,所以返回的是2。
typeof 2*3; // NaN
typeof (2*3); // 'number'
typeof 2+3; // 'number3'
// typeof的优先级比加减乘除还高,所以一般习惯把操作数加上括号
// 第二题
console.log(true || false && false) // true
// 这是一个很经典的例子,如果我们不知道 && 的优先级比 || 高,我们肯定以为返回值是false。
// 第三题
let number = 3
console.log(number + 4 > 6) // true
console.log(number * 2 > 5) // true
// 加减乘除的运算符优先级比大小于高,先计算加减乘除
// 第四题
console.log(1 && 0 ? 3 : 2) // 2
// &&的运算级比三元运算符更高,先计算&&
// 第五题
var a=0, b=0;
a+++b; // 0
a; // 1
b; // 0
// 右结合的优先级高于左结合和加号,上面会先找有没有右结合,找到a++先运算,所以运算顺序是(a++)+b。
// 第六题
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}
console.log(b); // {n:1, x:{n: 2}}