Skip to content
此页目录
本文总阅读量

JavaScript 运算符

ECMAScript 中的操作符比较独特,包括字符串、数值、布尔值,甚至还有对象。应用给对象时通常会调用会调用valueOf()toString()方法。

一元运算符

递增/递减

js
++n // 先自增 1,再运算
n++ // 先运算,再自增 1
--n // n-- 同理
!n // 转换为 Boolean 值

操作符在前,先自递增/递减后再进行运算。

js
let num1 = 2;
let num2 = 20;
let num3 = --num1 + num2;
let num4 = num1 + num2;
console.log(num3); // 21
console.log(num4); // 21

操作符在后,先运算再进行自递增/递减。

js
let num1 = 2;
let num2 = 20;
let num3 = num1-- + num2;
let num4 = num1 + num2;
console.log(num3); // 22
console.log(num4); // 21
js
let s1 = "2"; // 是有效的数值形式:则转换为数值再应用改变。
let s2 = "z"; // 不是有效的数值:则将变量的值设置为NaN 。
let b = false; // 如果是false,则转换为0 再应用改变。如果是true,则转换为1 再应用改变。
let f = 1.1; // 对于浮点值,加1 或减1。
let o = {  // 开头说过,如果是对象,valueOf()方法取得可以操作的值,再应用上面的规则。如果是NaN,则调用toString()并再次应用其他规则。
  valueOf() {
    return -1;
  }
};
s1++; // 值变成数值3
s2++; // 值变成NaN
b++; // 值变成数值1
f--; // 值变成0.10000000000000009(因为浮点数不精确)
o--; // 值变成-2

一个变量同时等于两个值

参考上面所说的,如果是对象,操作符会先调用 valueOf 取值,重写 valueOf 即可使 o == 1 且 o == 2

js
let o = {
  a:0,
  valueOf() {
    return ++this.a
  }
};
if(o == 1 && o == 2){ // true
  console.log('yes')
}
// yes

一元加和减

js
let s1 = "01";
let s2 = "1.1";
let s3 = "z";
let b = false;
let f = 1.1;
let o = {
  valueOf() {
    return -1;
  }
};
s1 = +s1; // 值变成数值1
s1 = -s1; // 值变成数值-1
s2 = +s2; // 值变成数值1.1
s2 = -s2; // 值变成数值-1.1
s3 = +s3; // 值变成NaN
b = +b; // 值变成数值0
f = +f; // 不变,还是1.1
f = -f; // 变成-1.1
o = +o; // 值变成数值-1

算术运算符

  • + 加法比较特殊

    • 两边都是数字时,做数学运算
    • 一边为字符串,进行字符串连接
    • 一边为对象类型 object,将对象使用 toString 方法转换为字符串,进行连接
  • - * / % 只能数学运算 隐式用 Number 转换 不能转 ---> NaN

js
alert([10] + 10);           // '1010'
alert([1,2,3] + 10);        // '1,2,310'
alert({name:'joth'} + 10);  // '[object Object]10'
alert(null + 10);           // 10
alert(undefined + 10);      // NaN
alert(true + 10);           // 11

alert(null - 10);           // -10
alert(undefined * 10);      // NaN
alert([10,20] / 10);        // NaN

alert(1 % 0);               // NaN
alert(100 % null);          // NaN

比较运算符

  • 大于 >,小于 <,大于等于 >=,小于等于 <=, 等于 ==,全等 ===,不等于 !=,不全等 !==。
  • == 等于时:只需要值相等,不用管数据类型,实际上也是通过 Number 进行类型转换
  • === 全等时:不会进行数据类型转换 那么需要两边的数据类型和值都相等
  • 特例 undefined == null 为真( js 真理)undefined 值是由null 值派生而来的,因此ECMA-262 将它们定义为表面上相等
js
'10' == 10// true
'10' === 10// false
undefined == null// true 
undefined === null// false 数据类型不同

逻辑运算符

  • 非 ! 取反 非真为假 非假为真
  • 与 && 与运算见假则假
  • 或 || 或运算见真则真
js
!false// true
!!false// false

// 与运算:与运算见假则假
true && false// false
false && true// false
false && false// false
true && true// true

// 或运算:或运算见真则真
true || false// true
false || true// true
true || true// true
false || false// false
10 > 3 && '10' > '3'// false

短路运算

  • 短路与: 第一个值为true 返回第二个值, 第一个值为false,则返回第一个值
  • 短路或: 第一个值为true 返回第一个值, 第一个值为false,则返回第二个值
js
// 短路与
10 && null// null
undefined && 'abc'// undefined

// 短路或
10 || null// 10
undefined || 'abc'// 'abc'

三目运算符

  • 方法: ? : ---> 判断条件 ? 当条件为真时 返回的值 : 当条件为假时返回的值
js
var y = -20 > 0 ? 1 : -20 == 0 ? 0 : -1;

赋值运算符

  • = 赋值 += -= *= /= %=
js
a += 5;  // 等价于 a = a + 5;
a -= 10; // 等价于 a = a - 10;
a *= 3  // 等价于 a = a * 3
a /= 2  // 等价于 a = a / 2
a %= 2  // 等价于 a = a % 2

隐式类型转换

  • + - * / %
  • + 转换方式比较多
  • - * / % 都是使用 Number 转数字 能转数字就运算 不能转数字就 NaN

括号/逗号运算符

js
var a = (1,2,2,1,0) // 0 返回最后一项
  • 应用
js
const arr = [{a: 1},{a: 2}];
arr.reduce((prev,next) => (prev+=2, prev + next.a),0) // 7

指数运算符

  • ES2016 新增了一个指数运算符(**)
js
2 ** 2 // 4
2 ** 3 // 8

2 ** 3 ** 2 // 512,相当于 2 ** (3 ** 2)
a **= 2; // 等同于 a = a * a;
b **= 3; // 等同于 b = b * b * b;

位操作符

  • ECMAScript中的所有数值都以IEEE 754 64 位格式存储

  • 但位操作并不直接应用到 64 位表示,而是先把值转换为 32 位整数,位操作之后再把结果转换为64 位。(所以只需要考虑 32 位)

  • 32 位,前面 31 位表示数值,32 位表示数值的符号,0 表示正,1 表示负。(称为符号位)

  • 正值以二进制格式存储,31 位都是 2 的幂。(第一位 2º,第二位 2¹,以此类推)

数值 18 用二进制来表示为 00000000000000000000000000010010(32 位数),前面的 0 可以省略 10010。

10010 = (2^4 * 1)+(2^3 * 0)+(2^2 * 0)+(2^1 * 1)+(2^0 * 0) = 18

  • 负数以二补数(补码)储存
    1. 以绝对值的二进制表示(-18先确定18的二进制)
    2. 0 变成 1,1 变成 0(称为补数/补码)
    3. 给结果加 1

按上述步骤表示 -18:

第一步:表示绝对值 18

0000 0000 0000 0000 0000 0000 0001 0010

第二步:补码

1111 1111 1111 1111 1111 1111 1110 1101

第三部:给补数加 1

1111 1111 1111 1111 1111 1111 1110 1110 (这就是 -18 的二进制表示)

js
let num = -18;
console.log(num.toString(2)); // '-10010'

输出'-10010',这个过程会求处二补数,然后符合逻辑的表示出来。ECMA中存在无符号的整数,也就是说无符号的整数比有符号的范围更大,因为符号位可以用来表示数值。

按位非

~ 来表示,作用是返回数值的补数。

js
let num1 = 25;    // 二进制00000000000000000000000000011001
let num2 = ~num1; // 二进制11111111111111111111111111100110 这里取反后还减了 1
console.log(num2); // -26

这样的结果比-num1 - 1结果更快,位操作符是在底层表示进行的。

应用

js
let a = 25.513
~~a // 25 取整
let b = 5.9
~~b // 5 取整

~a 反补减 1 得 -26,再~ 反补得到正 26 减 1 得到 25。

按位与

& 来表示,两个数的位 1 1 得 1,0 1 得 0, 0 0 得 0。

js
let result = 25 & 3;
console.log(result); // 1

25 = 0000 0000 0000 0000 0000 0000 0001 1001

  3 = 0000 0000 0000 0000 0000 0000 0000 0011

只有都为 1 时二进制位才取 1

0000 0000 0000 0000 0000 0000 0000 0001

所以结果就是 1

按位或

| 来表示,看懂了按位与那么按位或也是同理。有一个位为 1 则为 1,都为 0 时则为 0。

js
let result = 25 | 3;
console.log(result); // 27
// 25 = 0000 0000 0000 0000 0000 0000 0001 1001
//  3 = 0000 0000 0000 0000 0000 0000 0000 0011
// 得   0000 0000 0000 0000 0000 0000 0001 1011
// 11011 等于27

按位异或

^ 来表示,它只有在一位是 1,一位是 0 时才会得 1。都是 0 或都是 1 则得 0。

js
let result = 25 ^ 3;
console.log(result); // 26
// 25 = 0000 0000 0000 0000 0000 0000 0001 1001
//  3 = 0000 0000 0000 0000 0000 0000 0000 0011
// 得   0000 0000 0000 0000 0000 0000 0001 1010
// 二进制码11010 等于26

相同的两个值,按位异或比按位或得出的结果小 1

左移

<< 表示,二进制位向左移动的位数

js
let oldValue = 2; // 二进制表示 10
let newValue = oldValue << 5; // 二进制表示 1000000
console.log(newValue) // 十进制 64

2 的二进制 10,向左移 5,补了 5 个 0,1000000 即为 64。

但是左移会保留符号,-2 左移 5 得到 -64,并不是 64

有符号右移

>> 表示,与左移同理,也会保留符号。

js
let oldValue = 64; // 二进制表示 1000000
let newValue = oldValue >> 5; // 二进制表示 10
console.log(newValue) // 十进制 2

无符号右移

>>> 表示,

js
let oldValue = 64; // 二进制表示 1000000
let newValue = oldValue >>> 5; // 二进制表示 10
console.log(newValue) // 十进制 2

let oldValue = -64; // 二进制表示 11111111111111111111111111000000
let newValue = oldValue >>> 5; // 二进制表示 134217726
// 把符号位也当做值来位移了,导致结果相差很大

评论

交流群