【JavaScript】运算符

操作符

ECMAScript 中的操作符可用于各种值,包括字符串、数值、布尔值,甚至还有对象。

在应用给对象时,操作符通常会调用 valueOf / toString 方法来取得可以计算的值。



一元操作符

递增 & 递减

有两个版本:前缀版 ++num 和后缀版 num++。区别在于,++num 会先自增,再参与运算;num++ 会先参与运算,再自增。

let num1 = 1,
    num2 = 1;
const number1 = num1++, // 先参与运算,再自增
    number2 = ++num2; // 先自增,再参与运算
console.log(`number1 - ${number1}; `, `num1 - ${num1}`); // number1 - 1;  num1 - 2
console.log(`number2 - ${number2}; `, `num2 - ${num2}`); // number2 - 2;  num2 - 2

对于 string、boolean 类型的原始数据和 undefined、null,会被隐式转换成 number 类型:

let numStr = '2';
++numStr; // Number("2") → 2
console.log(numStr); // 3

let bool = false;
++bool; // Number(false) → 0
console.log(bool); // 1

let un = undefined;
++un; // Number(undefined) → NaN
console.log(un); // NaN

let n = null;
++n; // Number(null) → 0
console.log(n); // 1

对于引用类型的数据,会调用 valueOf / toString 方法来取得可以计算的值:

let o1 = {};
++o1; // Number({}) → NaN
console.log(o1); // NaN

let o2 = {
    valueOf() {
        return 1;
    },
    toString() {
        return -1;
    },
};
o2--;
console.log(o2); // 0 —— 说明 valueOf 优先级高于 toString

let o3 = {
    toString() {
        return -1;
    },
};
o3--;
console.log(o3); // -2

加 & 减

对于原始类型数据,相当于使用 Number() 进行类型转换:

let s1 = '01';
console.log(+s1); // 1

let b = false;
console.log(+b); // 0

let un = undefined;
console.log(+un); // NaN

let n = null;
console.log(+n); // 0

对于引用类型数据,会调用它们的 valueOf / toString 方法以得到可以转换的值:

let o = {
    valueOf() {
        return -1;
    },
};
console.log(+o); // -1



乘性操作符

ECMAScript 定义了 3 个乘性操作符:乘法 - *、除法 - /、取模 - %


乘法操作符规则

  • 如果操作数都是数值,则执行常规的乘法运算。如果 ECMAScript 不能表示乘积,则返回 Infinity 或 -Infinity。
  • 如果有任一操作数是 NaN,则返回 NaN。
  • 如果是 Infinity 乘以 0,则返回 NaN。
  • 如果是 Infinity 乘以非 0 的有限数值,则根据第二个操作数的符号返回 Infinity 或 -Infinity。
  • 如果是 Infinity 乘以 Infinity,则返回 Infinity。
  • 如果有不是数值的操作数,则先在后台用 Number() 将其转换为数值,然后再应用上述规则。

除法操作符规则

  • 如果操作数都是数值,则执行常规的除法运算。如果 ECMAScript 不能表示商,则返回 Infinity 或 -Infinity。
  • 如果有任一操作数是 NaN,则返回 NaN。
  • 如果是 Infinity 除以 Infinity,则返回 NaN。
  • 如果是 0 除以 0,则返回 NaN。
  • 如果是非 0 的有限值除以 0,则根据第一个操作数的符号返回 Infinity 或 -Infinity。
  • 如果是 Infinity 除以任何数值,则根据第二个操作数的符号返回 Infinity 或-Infinity。
  • 如果有不是数值的操作数,则先在后台用 Number() 函数将其转换为数值,然后再应用上述规则。

取模操作符规则

  • 如果操作数是数值,则执行常规除法运算,返回余数。
  • 如果被除数是无限值,除数是有限值,则返回 NaN。
  • 如果被除数是有限值,除数是 0,则返回 NaN。
  • 如果是 Infinity 除以 Infinity,则返回 NaN。
  • 如果被除数是有限值,除数是无限值,则返回被除数。
  • 如果被除数是 0,除数不是 0,则返回 0。
  • 如果有不是数值的操作数,则先在后台用 Number() 函数将其转换为数值,然后再应用上述规则。



加性操作符

加性操作符,即加法和减法操作符。


加法操作符规则

  • 如果有任一操作数是 NaN,则返回 NaN
  • 如果是 Infinity 加 Infinity,则返回 Infinity
  • 如果是 -Infinity 加 -Infinity,则返回 -Infinity
  • 如果是 Infinity 加 -Infinity,则返回 NaN
  • 如果是 +0 加 +0,则返回 0
  • 如果是 -0 加 +0,则返回 0
  • 如果是 -0 加 -0,则返回 -0

如果有一个操作数是字符串,则要应用如下规则:

  • 如果两个操作数都是字符串,则将第二个字符串拼接到第一个字符串后面
  • 如果只有一个操作数是字符串,则将另一个操作数转换为字符串,再将两个字符串拼接在一起

如果有任一操作数是对象、数值或布尔值,则调用它们的 toString 方法以获取字符串,然后再应用前面的关于字符串的规则。对于 undefined 和 null,则调用 String 函数,分别获取 “undefined” 和 “null”。


减法操作符规则

  • 如果是 Infinity 减 Infinity,则返回 NaN
  • 如果是 -Infinity 减 -Infinity,则返回 NaN
  • 如果是 Infinity 减 -Infinity,则返回 Infinity
  • 如果是 -Infinity 减 Infinity,则返回 -Infinity
  • 如果是 +0 减 +0,则返回 +0
  • 如果是 +0 减 -0,则返回 -0
  • 如果是 -0 减 -0,则返回 +0
  • 如果有任一操作数是字符串、布尔值、null 或 undefined,则先在后台使用 Number() 将其转换为数值,然后再根据前面的规则执行数学运算。如果转换结果是 NaN,则减法计算的结果是 NaN
  • 如果有任一操作数是对象,则调用其 valueOf() 方法取得表示它的数值。如果该值是 NaN,则减法计算的结果是 NaN。如果对象没有 valueOf() 方法,则调用其 toString() 方法,然后再将得到的字符串转换为数值。



布尔操作符

布尔操作符一共有:逻辑非 - !、逻辑与 - &&、逻辑或 - ||

对于非布尔类型的操作值,JS 会将其隐式转换为 boolean 类型。


基础用法

  • &&:只有两边都是 true 才返回 true,否则返回 false

  • ||:只有两边都是 false 才返回 false,否则返回 true

  • !:取反。(6 个负性值:0NaN""nullundefinedfalse; 其余值转布尔类型都为 true,取反则为 false

console.log(true && true); // true
console.log(false || false); // false
console.log(!true); // false
console.log(!''); // true

短路算法

&& 会先将第一个操作值转为 boolean 类型进行判断:

  1. 如果是 false,则直接返回第一个值,第二个值不执行
  2. 如果为 true,则执行并返回第二个值。
console.log(false && 5); // false
console.log(null && 哈哈); // null - 因为第二个值不执行,所以出错了也不会抛出
console.log(3 && 5); // 5
console.log(true && 'a'); // a

|| 会先将第一个值转为 boolean 类型进行判断:

  1. 如果为 true,则直接返回第一个值,第二个值不执行
  2. 如果为 false,则执行并返回第二个值。
console.log(false || 5); // 5
console.log(null || 'a'); // a
console.log(true || 'a'); // true
console.log(3 || 哈哈); // 3  - 因为第二个值不执行,所以出错了也不会抛出



关系操作符

关系操作符执行比较两个值的操作,包括小于 - <、大于 - >、小于等于 - <=、大于等于 - >=

如果比较运算成立,则返回 true;不成立 / 比较不了,则返回 false

10 > NaN; // false
10 > 2; // true

  • 如果操作数都是 number,则执行数值比较。
  • 如果操作数都是 string,则逐个比较字符串中对应字符的编码。
  • 如果有任一操作数是 number,则将另一个操作数转换为数值,执行数值比较。
  • 如果有任一操作数是 boolean,则将其转换为数值再执行比较。
  • 如果有任一操作数是对象,首先调用 ToPrimitive,如果结果出现非字符串,就根据 ToNumber 规则强制类型转换为数字来进行比较。
var a = [42];
var b = ['43'];

a < b; // true
b < a; // false
var a = ['42'];
var b = ['043'];

a < b; // false

ab 并没有被转换为数字,因为 ToPrimitive 返回的是字符串,所以这里比较的是 "42""043" 两个字符串,它们分别以 "4""0" 开头。因为 "0" 在字母顺序上小于 "4",所以最后结果为 false。

var a = [4, 2];
var b = [0, 4, 3];

a < b; // false

a 转换为 "4,2", b 转换为 "0,4,3",同样是按字母顺序进行比较。

var a = { b: 42 };
var b = { b: 43 };

a < b; // ? ?

结果还是 false,因为 a"[object Object]"b 也是 "[object Object]",所以按照字母顺序 a < b 并不成立。

var a = { b: 42 };
var b = { b: 43 };

a < b; // false
a == b; // false
a > b; // false

a <= b; // true
a >= b; // true

引用类型数据的相等比较,只有在两个引用指向同一个对象时才会返回 true。因为 ab 指向不同的对象,所以 a == b 返回 false。

根据规范 a <= b 被处理为 b < a,然后将结果反转。因为 b < a 的结果是 false,所以 a <= b 的结果是 true。



相等操作符

  1. 相等运算符 ==:比较是否相等,数据会发生隐式类型转换。
  2. 全等运算符 ===:比较 & 数据类型是否相等。
6 == '6'; // true
6 === '6'; // false

6 != '6'; // false
6 !== '6'; // true

  • 如果任一操作数是 boolean,则将其转换为 number 再比较是否相等。false - 0、true - 1。
var a = '42';
var b = true;

a == b; // false
  • 如果一个操作数是 string,另一个操作数是 number,则尝试将 string 👉 number,再比较是否相等。
var a = 42;
var b = '42';

a === b; // false
a == b; // true
  • 如果 Type(x) 是 number / string,Type(y) 是对象,则返回 x == ToPrimitive(y) 的结果。
var a = 42;
var b = [42];

a == b; // true

[42] 首先调用 ToPrimitive 抽象操作,返回 "42",变成 "42" == 42,然后又变成 42 == 42,最后二者相等。

var a = 'abc';
var b = Object(a); // 和 new String(a) 一样

a === b; // false
a == b; // true

a == b 结果为 true,因为 b 通过 ToPrimitive 进行强制类型转换,并返回标量基本类型值 "abc",与 a 相等。

var a = null;
var b = Object(a); // 和 Object() 一样
a == b; // false

var c = undefined;
var d = Object(c); // 和 Object() 一样
c == d; // false

var e = NaN;
var f = Object(e); // 和 new Number(e) 一样
e == f; // false

因为没有对应的封装对象,所以 nullundefined 不能够被封装,Object(null)Object() 均返回一个常规对象。

NaN 能够被封装为数字封装对象,但拆封之后 NaN == NaN 返回 false,因为 NaN 不等于 NaN。


注意事项:

  • 比较运算符不要连续使用。因为比较运算返回的是 boolean 值,可能会影响实际的比较结果
console.log(3 > 2 > 1); // 3 > 2 返回 true, Number(true) 为 1, 所以最终返回 false
  • 特别的 1nullundefined 不能转换为其他类型的值再进行比较
console.log(null == 0); // false
console.log(undefined == 0); // false
  • 特别的 2nullundefined 相等,但不全等(null === nullundefined === undefined
console.log(null == undefined); // true → 因为 undefined 继承自 Null
console.log(null === undefined); // false → null 是 Null 类型,undefined 是 underfined 类型
  • 特别的 3NaN != 任何值 ( 连自己都不等于 )
console.log(NaN == NaN); // false

比较少见的情况

  1. 返回其他数字
Number.prototype.valueOf = function () {
    return 3;
};

new Number(2) == 3; // true

Number(2) 涉及 ToPrimitive 强制类型转换,因此会调用 valueOf()


  1. 假值的相等比较
'0' == null; // false
'0' == undefined; // false
'0' == false; // true -- 晕!
'0' == NaN; // false
'0' == 0; // true
'0' == ''; // false

false == null; // false
false == undefined; // false
false == NaN; // false
false == 0; // true -- 晕!
false == ''; // true -- 晕!
false == []; // true -- 晕!
false == {}; // false

'' == null; // false
'' == undefined; // false
'' == NaN; // false
'' == 0; // true -- 晕!
'' == []; // true -- 晕!
'' == {}; // false

0 == null; // false
0 == undefined; // false
0 == NaN; // false
0 == []; // true -- 晕!
0 == {}; // false

  1. 极端情况
[] == ![]; // true

根据 ToBoolean 规则,它会进行布尔值的显式强制类型转换。所以 [] == ![] 👉 [] == false 👉 [] == 0 👉 '' == 0 👉 0 == 0 👉 true

2 == [2]; // true
'' == [null]; // true

介绍 ToNumber 时我们讲过,== 右边的值 [2][null] 会进行 ToPrimitive 强制类型转换。因为数组的 valueOf() 返回数组本身,所以强制类型转换过程中数组会进行字符串化,以便能够和左边的基本类型值进行比较。

第一行中的 [2] 会转换为 "2",然后通过 ToNumber 转换为 2。第二行中的 [null] 会直接转换为 ""。所以最后的结果就是 2 == 2"" == ""



赋值操作符

= += -= *= /= %= >>= <<= >>>=

简单赋值用等于号(=)表示,将右手边的值赋给左手边的变量。

复合赋值使用乘性、加性或位操作符后跟等于号(=)表示。

num += n; // 等价于 num = num + n;
num += n + 1; // 等价于 num = num + n + 1;



条件操作符

格式:variable = boolean_expression ? true_value : false_value;

let max = num1 > num2 ? num1 : num2;

上例表示:如果 num1 大于 num2 (条件表达式为 true),则将 num1 赋给 max;否则,将 num2 赋给 max



指数操作符

ES7 新增的指数操作符 **数值 ** 次方数,相当于 Math.pow(数值, 次方数)

2 ** 2; // 2 的 2 次方 = 4
2 ** 3; // 2 的 3 次方 = 4

指数操作符可以与赋值操作符 = 结合,形成一个新的赋值操作符 **=

a **= 2; // 等同于 a = a ** 2;
b **= 3; // 等同于 b = b ** 3;



逗号操作符

逗号操作符可以用来在一条语句中执行多个操作,如下所示:

const num1 = 1,
    num2 = 2,
    num3 = 3;

在一条语句中同时声明多个变量是逗号操作符最常用的场景。不过,也可以使用逗号操作符来辅助赋值。在赋值时使用逗号操作符分隔值,最终会返回表达式中最后一个值:

const num = (5, 1, 4, 8, 0); // num 的值为 0

在这个例子中,num 将被赋值为 0,因为 0 是表达式中最后一项。逗号操作符的这种使用场景并不多见,但这种行为的确存在。



位操作符

位操作符用于操作二进制数据。它们对操作数中的每个位进行操作,并生成新的二进制结果。

  1. 按位与(&):当两个位都为 1 时,结果位为 1;否则为 0。
const a = 5; // 二进制表示为 0101
const b = 3; // 二进制表示为 0011
const result = a & b; // 二进制 0001,结果为 1
console.log(result); // 1
  1. 按位或(|):当两个位中至少有一个位为 1 时,结果位为 1;否则为 0。
const a = 5; // 二进制表示为 0101
const b = 3; // 二进制表示为 0011
const result = a | b; // 二进制 0111,结果为 7
console.log(result); // 7
  1. 按位异或(^):当两个位不相同时,结果位为 1;否则为 0。
const a = 5; // 二进制表示为 0101
const b = 3; // 二进制表示为 0011
const result = a ^ b; // 二进制 0110,结果为 6
console.log(result); // 6
  1. 按位非(~):将每个位取反。对于有符号整数,按位非运算将其转换为补码形式。
const a = 5; // 二进制表示为 0000 0000 0000 0101
const result = ~a; // 二进制 1111 1111 1111 1010,结果为 -6
console.log(result); // -6
  1. 左移(<<):将操作数的二进制表示向左移动指定的位数。左移时,右侧空出的位用 0 填充。
const a = 5; // 二进制表示为 0000 0000 0000 0101
const result = a << 2; // 二进制 0000 0000 0001 0100,结果为 20
console.log(result); // 20
  1. 右移(>>):将操作数的二进制表示向右移动指定的位数。右移时,左侧空出的位根据操作数的符号进行填充。
const a = -10; // 二进制表示为 1111 1111 1111 0110
const result = a >> 2; // 二进制 1111 1111 1111 1101,结果为 -3
console.log(result); // -3
  1. 无符号右移(>>>):将操作数的二进制表示向右移动指定的位数。右移时,左侧空出的位用 0 填充。
const a = -10; // 二进制表示为 1111 1111 1111 0110
const result = a >>> 2; // 二进制 0011 1111 1111 1101,结果为 1073741821
console.log(result); // 1073741821



运算符优先级

以下是一些常见运算符按照优先级从高到低的顺序:

  1. 小括号 ():括号用于分组运算,具有最高的优先级。
  2. 指数运算符 **:指数运算符用于进行指数运算。
  3. 一元运算符:包括逻辑非 !、正号 +、负号 -、位非 ~ 等。
  4. 乘法、除法和取模运算符:*/%
  5. 加法和减法运算符:+-
  6. 位移运算符:左移 <<、右移 >> 和无符号右移 >>>
  7. 关系运算符:大于 >、小于 <、大于等于 >=、小于等于 <=instanceofin 等。
  8. 相等性运算符:严格相等 ===、严格不等 !==、抽象相等 ==、抽象不等 !=
  9. 位运算符:按位与 &、按位或 |、按位异或 ^
  10. 逻辑运算符:逻辑与 && 和逻辑或 ||
  11. 条件运算符:条件运算符 condition ? valueIfTrue : valueIfFalse
  12. 赋值运算符:赋值 =、加等于 +=、减等于 -=、乘等于 *= 等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JS.Huang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值