JavaScript 运算符,也叫操作符
-
- 对一个或者多个值进行运算,都是会返回结果的。
- 比如:
- typeof 返回一个值的类型,返回值类型是字符串。
隐式类型转换:
- 任意值 = 任意值 + ""; // 就能转换成字符串
- 任意值 = 任意值 - 0; // 就能转换成Number
- 任意值 = +任意值; // 就能转换成 Number,更简洁
- 任意值 = !!任意值; // 即可将 任意值 转换成布尔类型的值
- 算术运算符,
不会对等号右边的值产生影响;
对非 Number 的值进行运算时,都会先转换成 Number 再运算,除非有字符串进行运算;
任何值与 NaN 进行运算,都得 NaN 。
+ 对两个值进行相加,返回一个数字 Number ,但是又字符串进行运算会返回一个字符串
-
-
- 两个字符串相加,会拼串成一个字符串:
- b = "123"+"456"; // b 还是一个字符串,"123456"
- 任何值和字符串进行运算,都会先转换成字符串,然后拼串:
-
c = 123+"4"; // c 等于字符串"1234" result = 1 + 2 + '3'; // result 为字符串 '33' result = '1' + 2 + 3; // result 为字符串 '123'
-
- 两个字符串相加,会拼串成一个字符串:
-
- 啊加法运算符是在运行时决定,到底是执行相加,还是执行连接。
- 也就是说,运算子的不同,导致了不同的语法行为,这种现象称为 “重载”(overload)
- 运算子是对象,必须先转成原始类型的值,然后再相加
- 对象转成原始类型的值
- 首先,自动调用对象的
valueOf
方法,对象的valueOf
方法总是返回对象自身 - 再自动调用对象的
toString
方法,将其转为字符串-
var obj = { p: 1 }; obj.valueOf() // { p: 1 } obj.valueOf().toString() // "[object Object]"
-
-
自己定义
valueOf
方法或toString
方法,得到想要的结果。var obj = { valueOf: function () { // 重写 .valueOf() return 1; } }; obj + 2 // 3 var obj = { toString: function () { // 重写 .toString() return 'hello'; } }; obj + 2 // "hello2"
- 首先,自动调用对象的
- 对两个值进行相减,返回一个数字 Number
* 对两个值进行相乘,返回一个数字 Number
/ 对两个值进行相除,返回一个数字 Number
% 对两个值进行取余,返回一个数字 Number
- 为了得到负数的正确余数值,可以先使用绝对值函数
-
// 正确的写法 function isOdd(n) { return Math.abs(n % 2) === 1; } isOdd(-5) // true isOdd(-4) // false
-
- 可以用于浮点数的运算。但是,由于浮点数不是精确的值,无法得到完全准确的结果
** 指数运算符
- 前一个运算子是底数,后一个运算子是指数
-
2 ** 4 // 16 即 2 的 4 次方
-
- 多个指数运算符连用时,先进行最右边的计算
-
2 ** 3 ** 2 // 512 相当于 2 ** (3 ** 2)
-
- 一元运算符,
只需要一个操作数
对于非数字运算,会先转换成 Number 再进行运算
-
- + 正号 不会对操作数产生任何影响
-
var result = 1 + +'2' + 3; // result 为 Number 类型的 6
-
- + 正号 不会对操作数产生任何影响
- - 负号 会对数组进行符号的取反
- ++ 自增1
-
var a = 1; a++; // a 在这条语句执行完以后,a = 2 ++a; // a 在这条语句执行过程中 a = 2
-
- -- 自减1
-
var b = 2; result = b-- + b; // b = 1 , result = 3 result = --b + b; // b = 1, result = 2
- js 的三种逻辑运算符 && || !
- 对操作值无影响
- 对于非布尔值进行
- 与运算时,会先将其转换成布尔值比较,再返回原值。
- 若两个值都是 true,则返回后边的值;若两个值中有 false,则返回前面的false 。
- 若第一个值为 true,则必然返回第二个值;若第一个值为 false,则返回第一个值。
- 或运算,会先将其转换成布尔值运算,再返回原值。
- 若第一个值为 true,则返回第一个值;若第一个值为 false,则必然返回第二个值。
! 对右侧的值进行非运算,并返回布尔类型的值:true 变 false;false 变 true
-
-
-
var a = false; a = !!a; // 如果对一个值进行两次取反,不会发生变化。
-
var b = 10; b = !b; // 对非布尔类型的值进行非运算,会先转换成 Boolean 类型,再进行运算
可以利用这个特性进行隐式类型转换,b = !!b; // 即可将 b 变成一个布尔值
-
-
&& 对符号两侧的值进行与运算,并返回布尔类型的值:只要有一个值为 false ,就返回 false
- 如果第一个值为 false,则直接返回它的值,不会再对第二个运算子进行操作。 称 "短路 short cut"
- 有些程序员喜欢用它取代 if 结构
-
if (i) { doSomething(); } // 等价于 i && doSomething();
|| 对符号两侧的值进行或运算,并返回布尔类型的值:只要有一个值为 true,就返回 true
-
-
- 如果第一个值为 true,则不会再判断第二个值了。
-
- 赋值运算符
= 将 右侧的值 赋给 左侧的变量
- 与算术运算符的结合
-
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 x ^= y // 等同于 x = x ^ y
- 关系运算符
比较两个值之间的大小关系,关系成立则返回 true,关系不成立则返回 false 。
对于非数字进行比较,则先转换成数字再比较。。。
如果两个字符串比较,不会转换成数字,会比较Unicode 。。。一位一位比,出结果就不比了。。。可以进行英文名排序。
任何值和 NaN 进行任何比较,都返回 false 。
如
-
result = 5 < 4; // 关系不成立,result 为false 。 // 当 console.log("123456" < "5"); // 希望得到一个数字上的正确比较 // 则可以写成 console.log("123456" < +"5"); // 即,为其中一个字符串加 "+" 转换成数字,再进行比较
>
==
!=
<
>=
<=
undefined
和null
与其他类型的值比较时,结果都为false
,它们互相比较时结果为true
- 相等运算符 ==
比较两个值是否相等。
相等返回true,不等返回false
-
var a = 10; console.log(a == 2); // 很明显 打印 false
- 如果两个值的类型不同时,会自动进行类型转换,
-
console.log('1' == 1); // 会打印 true
- 具体转换成什么类型是不一定的,大部分情况转换成 Number
-
在上面的例子里,是将 '1' 转换成了 Number 类型。
-
console.log(null == 0); // 这是特殊情况,没有转换成 Number,所以打印 false
console.log(null == undefined); // 这也是特殊情况,由于 undefined 衍生自 null,所以打印 true
console.log(NaN == NaN); // 这也是特殊情况,NaN 不和任何值相等,包括它本身,所以打印 false
可以通过 isNaN() 函数判断一个值是否是NaN
var b = NaN;
console.log(isNaN(b)); // 如果是 NaN 返回true,否则返回 false
-
- 不相等运算符 !=
比较两个值是否不相等。
不等返回true,相等返回false
如果两个值的类型不同时,会自动进行类型转换,再进行比较。
- 条件运算符,也叫 三元运算符
- 语法
- (条件表达式)?:(语句1):(语句2);
- 在需要返回值的场合,只能使用三元条件表达式,而不能使用
if..else
- 流程:
- 首先对表达式进行求值
- 所得值为 true,则执行语句1,并返回执行结果
- 所得值为 false,则执行语句2,并返回执行结果
- 首先对表达式进行求值
- 实例
-
var a = 10; var b = 20; a > b : alert('a大'):alert('b大'); /***************************/ // 获取 a 和 b 中的最大值 var max = a > b ? a:b; // 获取 a , b , c 中的最大值 var max = ( a > b)? (a>c?a:c):(b>c?b:c);
-
- 如果 条件表达式 非布尔值,会转换成布尔值
- 其他运算符
=== 全等
- 判断两个值是否全等
- 不会进行类型转换,如果类型不同,直接返回 false
-
console.log(1 === '1'); // false console.log(null === undefined); // false
- 两个复合类型(对象、数组、函数)的数据比较时,不是比较它们的值是否相等,而是比较它们是否指向同一个地址。
-
{} === {} // false [] === [] // false (function () {} === function () {}) // false // 结果都是不相等 // 原因是对于复合类型的值,严格相等运算比较的是,它们是否引用同一个内存地址 // 而运算符两边的空对象、空数组、空函数的值,都存放在不同的内存地址,结果当然是false
- 如果两个变量引用同一个对象,则它们相等
-
var v1 = {}; var v2 = v1; v1 === v2 // true
-
undefined
和null
与自身严格相等- 两个只声明未赋值的变量是相等的 都是 undefined
!== 不全等
-
- 判断两个值是否不全等,
- 不会进行类型转换,如果类型不同,直接返回 true
-
console.log(1 !== '1'); // true console.log(null !== undefined); // true
1 !== '1' // true // 等同于 !(1 === '1')
void 运算符
作用是执行一个表达式,然后不返回任何值,或者说返回undefined
- 优先级很高
-
void 4 + 7 //
实际上等同于(void 4) + 7
void (x = 5) // 执行完 x=5 后 , 返回 undefined
-
- 主要作用
- 浏览器的书签工具(bookmarklet),
- 在超级链接中插入代码防止网页跳转。
-
<a href="javascript: void(f())">文字</a> <a href="javascript: void(document.form.submit())"> 提交 </a>
-
- 逗号运算符,
- 使用 ',' 可以分割多个语句,一般可以在声明变量时使用。
-
var a , b , c; var a = 1 , b , c = 3;
- 用于对两个表达式求值,并返回后一个表达式的值
-
'a', 'b' // "b" var x = 0; var y = (x++, 10); x // 1 y // 10
在返回一个值之前,先进行一些辅助操作
var value = (console.log('Hi!'), true); // Hi! value // true
-
运算符的优先级
- 在 js 中有一个 运算符优先级 的表,越靠上优先级越高
- 表并不需要记忆,遇到优先级不清楚的,可以使用括号改变优先级。
二进制位运算符
用于直接对二进制位进行计算,一共有7个
| 或运算符 or 两个二进制位都为0,则结果为0
- x = x | 0 保持原位不变
- (不管是整数或小数)转为32位整数
-
function toInt32(x) { return x | 0; } toInt32(1.001) // 1 toInt32(1.999) // 1 toInt32(1) // 1 toInt32(-1) // -1 toInt32(Math.pow(2, 32) + 1) // 1 toInt32(Math.pow(2, 32) - 1) // -1
-
& 与运算符 and 两个二进制位都为1,则结果为 1
-
0 & 3 // 0
- 0(二进制
00
)和3(二进制11
)进行二进制与运算会得到00
~ 否运算符 not 对一个二进制位取反
-
~ 3 // -4 可以理解成 -3减一
- 对
3
进行二进制否运算,得到-4
。之所以会有这样的结果,是因为位运算时,JavaScirpt 内部将所有的运算子都转为32位的二进制整数再进行运算 -
// 3的32位整数形式是00000000000000000000000000000011 // 二进制否运算以后得到11111111111111111111111111111100 // 由于第一位(符号位)是1,所以这个数是一个负数 // JavaScript 内部采用补码形式表示负数, // 即需要将这个数减去1,再取一次反,然后加上负号,才能得到这个负数对应的10进制值 // 这个数减去1等于11111111111111111111111111111011,再取一次反得到00000000000000000000000000000100,再加上负号就是-4
- 对一个整数连续两次二进制否运算,得到它自身。
- 二进制否运算遇到小数时,也会将小数部分舍去,只保留整数部分。
- 对一个小数连续进行两次二进制否运算,能达到取整效果
-
~~2.9 // 2 ~~47.11 // 47 ~~1.9999 // 1 ~~3 // 3 // 使用二进制否运算取整,是所有取整方法中最快的一种
- 对于其他类型的值, 进行二进制否运算,JavaScript 引擎会先调用
Number
函数,将字符串转为数值
^ 异或运算符 xor 两个二进制位不同,则结果为 1
- 连续对两个数
a
和b
进行三次异或运算,a^=b; b^=a; a^=b;
,可以互换它们的值 -
// 在不引入临时变量的前提下,互换两个变量的值 var a = 10; var b = 99; a ^= b, b ^= a, a ^= b; a // 99 b // 10
这是互换两个变量的值的最快方法。
- 异或运算也可以用来取整
-
12.9 ^ 0 // 12
-
<< 左移运算符 left shift
- 表示将一个数的二进制值向左移动指定的位数,尾部补
0
,即乘以 2 的指定次方。 -
// 4 的二进制形式为100, // 左移一位为1000(即十进制的8) // 相当于乘以2的1次方 4 << 1 // 8 -4 << 1 // -8
- 左移0位,就相当于将该数值转为32位整数,等同于取整,对于正数和负数都有效
-
13.5 << 0 // 13 -13.5 << 0 // -13
- 将颜色的 RGB 值转为 HEX 值
-
// RGB to HEX // (1 << 24)的作用为保证结果是6位数 // 自定义 将颜色的 RGB 值转为 HEX 值 var rgb2hex = function(r, g, b) { return '#' + ((1 << 24) + (r << 16) + (g << 8) + b) .toString(16) // 先转成十六进制,然后返回字符串 .substr(1); // 去除字符串的最高位,返回后面六个字符串 }
>> 右移运算符 right shift
- 将一个数的二进制值向右移动指定的位数,头部补
0
,即除以 2的指定次方(最高位即符号位不参与移动) -
4 >> 1 // 2 // 因为4的二进制形式为 00000000000000000000000000000100, // 右移一位得到 00000000000000000000000000000010, // 即为十进制的2 -4 >> 1 // -2 // 因为-4的二进制形式为 11111111111111111111111111111100, // 右移一位,头部补1,得到 11111111111111111111111111111110, // 即为十进制的-2
- 右移运算可以模拟 2 的整除运算
-
5 >> 1 // 相当于 5 / 2 = 2 21 >> 2 // 相当于 21 / 4 = 5 21 >> 3 // 相当于 21 / 8 = 2 21 >> 4 // 相当于 21 / 16 = 1
>>> 带符号位的右移运算符 zero filled right shift
- 表示将一个数的二进制形式向右移动,包括符号位也参与移动,头部补
0
。所以,该运算总是得到正值。 - 对于正数,该运算的结果与右移运算符(
>>
)完全一致,区别主要在于负数。 - 是查看一个负整数在计算机内部的储存形式,最快的方法
-1 >>> 0 // 4294967295 // -1作为32位整数时,内部的储存形式使用无符号整数格式解读,值为 4294967295(即(2^32)-1,等于11111111111111111111111111111111)
- 位运算符直接处理每一个比特位(bit),所以是非常底层的运算,好处是速度极快,缺点是很不直观
- 虽然在 JavaScript 内部,数值都是以64位浮点数的形式储存,但是做位运算的时候,是以32位带符号的整数进行运算的,并且返回值也是一个32位带符号的整数
- 位运算符只对整数起作用,如果一个运算子不是整数,会自动转为整数后再执行
- 开关作用
- 位运算符可以用作设置对象属性的开关
-
// 位运算符可以用作设置对象属性的开关 // 可以设置一个四位的二进制数,它的每个位对应一个开关 var FLAG_A = 1; // 0001 var FLAG_B = 2; // 0010 var FLAG_C = 4; // 0100 var FLAG_D = 8; // 1000
-
// 与运算 检验当前设置 是否打开了指定开关 var flags = 5; // 二进制的0101 if (flags & FLAG_C) { // 0101 & 0100 => 0100 => true // 如果打开了就...... }
-
// 假设需要打开A、B、D三个开关,我们可以构造一个掩码变量 var mask = FLAG_A | FLAG_B | FLAG_D; // 0001 | 0010 | 1000 => 1011
-
flags = flags & mask; // 与运算可以将当前设置中凡是与开关设置不一样的项 flags = flags ^ mask; // 异或运算可以切换(toggle)当前设置,即第一次执行可以得到当前设置的相反值,再执行一次又得到原来的值。 flags = ~flags; // 否运算可以翻转当前设置,即原设置为0,运算后变为1;原设置为1,运算后变为0