JS笔记《运算符》

85 阅读8分钟

算术运算符

  • JS共提供10个算术运算符:
    • 加法运算符x + y
    • 减法运算符: x - y
    • 乘法运算符: x * y
    • 除法运算符:x / y
    • 指数运算符:x ** y
    • 余数运算符:x % y
    • 自增运算符++x 或者 x++
    • 自减运算符--x 或者 x--
    • 数值运算符: +x
    • 负数值运算符:-x

加减乘除 + - * /

  • 加法运算符是在运行时决定到底是相加还是字符串拼接。而 - * / 则不会如此,它们的规则是两侧运算子一律转为数值,再进行相应的运算。
false + 'a' // 'falsea' 有一个是字符串时,则另一个会转成字符串后再连
true + true // 2  
  • 原始值相加: 运算子如果有字符串则是字符串拼接,如果没有则转为数值,隐式调用Number()然后相加。
true + true   // 2
true + null   // 0
null + undefined  // NaN
true + 3.14   // 4.140000000000001(精度问题)
true + '3.14' // 'true3.14'
  • 引用值相加:运算子如果是引用值,会先转为原始值然后再相加。先调用valueOf(),如果返回的还是对象,再调用toString()。再进行字符串拼接。
var obj = { p: 1 };
obj + 2  // "[object Object]2"

// 1. 先调用对象的 valueOf()方法,对象的valueOf()总是返回对象自身
obj.valueOf()  // {p: 1}
// 2. 再调用对象的toString方法,将其转为字符串
obj.valueOf().toString() // "[object Object]"

var arr = [1,2,3];
arr + 1        // '1,2,31'
arr + true     // '1,2,3true'

// 先调用valueOf(),后调用toString()
arr.valueOf()  // [1, 2, 3]
arr.valueOf().toString()  // '1,2,3'
  • 浮点数参与运算会出现精度问题,需要转成整数然后再除回来。
/**
 * 参数:数字、n次幂
 */
function formatNum(num, n){
  var mul = parseInt(num * Math.pow(10, n));  // 将小数乘以10的n次幂(转为整数)并取整
  return mul / Math.pow(10, n);               // 再除以10的n次幂,得到的结果为正确的结果
}

var total = 0.1 + 0.2;
console.log(total === 0.3);   // false

var res = formatNum(total, 1);
console.log(res === 0.3)  // true 

余数 %

12 % 5 // 2
3 % 4  // 3

// 运算结果的正负号由第一个运算子的正负号决定
-1 % 1 // -1
1 % -5 // 1

自增自减 ++ --

  • 前置递增(递减):先自加(自减),后返回值;后置递增(递减):先返回值,后自加、自减
  • 此运算符会将运算子首先转为数值,与Number()函数的作用相同,然后再自增或自减。
var x = true;
x++; // Number(true) => 1
x // 2

正负 + -

  • 此运算符会将运算子转为数值,与Number()函数的作用相同。
+true;  // 1  Number(true) => 1
-true;  // -1 Number(true) => 1 => -1
-null   // -0 Number(null) => 0
+[]     // 0 Number([]) => 0

+undefined // NaN Number(undefined) => NaN
+{}    // NaN Number({}) => NaN

指数 **

  • 前一个运算子是底数,后一个运算子是指数。当多个指数运算符连用时,先进行最右边的计算。
3 ** 1 // 3  3
3 ** 2 // 9  3 * 3
3 ** 3 // 27 3 * 3 * 3

2 ** 3 ** 2  // 521 相当于  2 ** (3 ** 2)

赋值 = += -= *= /= %= **=

// 等同于 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

{} 参与运算的情况

[] + {}  // '[object Object]'
// 解析流程如下:
// 1. [].valueOf().toString() === ''
// 2. ({}).valueOf().toString() === '[object Object]'
// 3. '' + '[object Object]' === '[object Object]'
  • 以上解析结果没问题,但如果把{}放在语句开头呢?
{} + []  // 0
  • 出问题了,为什么等于0?这是因为一条语句如果以{}开头,JS会把{}解析成一个空的代码块,并忽略它。相当于直接计算+[][]转为了空串,+又把空串转为了数字,所以结果为0
{} + {} // NaN
  • 还是以上原因,相当于直接计算+{}Number({}) === NaN,所以结果为NaN
({}) + [] // '[object Object]'
({}) + {} // '[object Object][object Object]'
// 解析流程如下:
// 1. ({}).valueOf() === {}
// 2. ({}).toString() === '[object Object]'
// 3. '[object Object]' + '[object Object]' === '[object Object][object Object]'
  • 用圆括号将{}包起来,让JS将这段解析成一个表达式就可以正常得到理想中的结果了。
{} - 1   // -1 相当于直接计算 -1
({}) - 1 // NaN 相当于 '[object Object]' - 1 

比较运算符

  • JS提供了8个比较运算符:
    • 大于运算符 x > y
    • 小于运算符 x < y
    • 大于等与运算符 x >= y
    • 小于等于运算符 x <= y
    • 相等运算符 ==
    • 严格相等运算符 ===
    • 不等运算符 !=
    • 严格不等运算符 !==

大于 小于 大于等于 小于等于 > < >= <=

  • 两个字符串比较的是Unicode字符集码点的大小,如果第一个字符相等,再依次对比。
'cat' > 'dog'   // false
'cat' > 'catalog' // false
'大' > '小'     // false  '大'的码点为22823;'小'的码点为23657
'10' > '11'     // false  '1'的码点为49,第一位相等,开始比较第二位:'0'的码点为48,小于'1'
  • 两个原始值比较,先转成数值,调用Number()函数后再比较。
5 > '4'       // true Number('4') => 4 
true > false  // true Number(true) => 1   Number(false) => 0
2 > true      // true
2 > '2a'      // false Number('2a') => NaN
2 > undefined // false Number(undefined) => NaN
2 > null      // true  Number(null) => 0

NaN与任何值比较都不相等,包括自身。

  • 如果有对象,会转为原始值。先调用valueOf(),如果返回的还是对象,再调用toString()。如果两个值都是字符串类型,则直接比较Unicode码点;如果不是,则转为数值后再比较。
var x = [2];
x > '11' // true
// 等同于 [2].valueOf().toString() > '11'
//        '2' > '11',开始比较Unicode码点

var y = [2];
y > 1; // true
// 等同于 [2].valueOf().toString() > 1
//       Number('2') > 1
//       2 > 1  true

var obj = {};
obj > '1' // true
// 等同于 {}.valueOf().toString() > '[object Object]'
// 即 '[object Object]' > '1',开始比较Unicode码点

严格相等 ===

  • 没有类型转换,如果两个值的类型不一样则直接返回false
  • 只有类型相同并且值也相同才返回true
1 === '1'       // false
true === 'true' // false

1 === 1         // true
+0 === -0       // true 特殊

undefined === undefined // true
null === null   // true

NaN与任何值比较都不相等,包括自身。

  • 两个对象(对象、数组、函数)比较时,比较的是他们是否指向同一个内存地址
{} === {}  // false
[] === []  // false
(function(){} === function(){})  // false

var obj = {};
var obj2 = obj;
obj2 === obj  // true obj 和 obj2指向的同一个内存地址

严格不相等 !==

  • !==的算法是先求===的结果,然后取反
1 !== '1'  // true  相当于: !(1 === '1')

相等运算符 ==

  • 比较不同类型的数据时,先进行类型转换,再用严格相等比较。
  • 比较不同类型的数据时,先进行类型转换,再用严格相等比较。
  • 比较不同类型的数据时,先进行类型转换,再用严格相等比较。
  • 原始值类型转换:Number()
1 == true      // true Number(true) => 1
0 == false     // true Number(false) => 0
'true' == true // Number('true') => NaN  Number(true) => 1
'' == 0        // true Number('') => 0
'123' == 123   // true 
  • 对象与原始值比较的类型转换:先调用valueOf(),如果返回的还是对象,再调用toString(),得到的值类型如果与原始值类型相同,则直接调用严格相等比较。如果类型不同,则再调用 Number()
[1] == 1 // true
// 1.  [1].valueOf().toString() =>  '1'
// 2.  Number('1') => 1
// 3.  1 === 1 两个原始值类型相同,直接严格相等比较,返回 true

[1,2] == '1,2' // true
// 1.  [1,2].valueOf().toString() =>  '1,2'
// 2.  '1,2' === '1,2' 两个原始值类型相同,直接严格相等比较,返回 true

[1] == true // true
// 1.  [1].valueOf().toString() =>  '1'
// 2.  Number('1') => 1
// 3.  Number(true) => 1
// 4.  1 === 1 两个原始值类型相同,直接严格相等比较,返回 true
  • null 和 undefined
undefined == undefined // true
null == null // true
undefined == null // true

false == null // false
false == undefined // false

0 == null // false
0 == undefined // false

undefinednull只有与自身比较,或者互相比较时,才会返回true;与其他任何类型的值比较时,都返回false

不相等运算符 !=

  • 先求相等运算符的结果,然后取反
1 != '1' // false  相当于 !(1 == '1')

逻辑运算符

  • 用于将表达式转为boolean,一共包含4个:
    • 与运算符 &&
    • 或运算符 ||
    • 非运算符 !
    • 三元运算符 ?:
  • 0、null、undefined、""、NaN、false转为boolean值都是false

与运算符 &&

  • 两侧都为true返回true,一侧为false返回false
1 && 0  // 0
0 && 1  // 0
1 && console.log(123); //123

&& 遇 false 或最后一个值时直接返回该值。

或运算符 ||

  • 两侧都为false返回false,一侧为true返回true
1 || 0  // 1
0 || 1  // 1
0 || false || 1;  // 1

|| 遇 true 或最后一个值时直接返回该值。

非运算符 !

  • 将boolean值转为相反值。
!123  // false  
!""   // true
!!123 // true  相当于把一个数据变成 boolean 数据

三元运算符 ?:

  • 三元运算符是JS中唯一一个需要三个运算子的运算符。
't' ? 'hello' : 'world' // "hello"
0 ? 'hello' : 'world' // "world"

void 运算符

  • 用于执行一个表达式,但是不返回任何值,也可以说是返回 undefined
void(1+1) // undefined
  • 一般用于<a>标签上,防止点击后网页跳转。
<a href="javascript:void(0);" οnclick="alert('text...')">  </a>

逗号运算符 ,

  • 用于对两个表达式求值,并返回后一个表达式的值。
var x = 0;
var y = (x++, 10);  // 先计算x++,然后返回10,给y赋值
x // 1 
y // 10

// 逗号运算符的一个用途是,在返回一个值之前,进行一些辅助操作
var value = (console.log('Hi!'), true);  // Hi!
value // true