本文章版权归饥人谷_Lyndon和饥人谷所有,转载请注明出处。
运算符是数据处理的基础,能够从现有数据中获得新的数据。JS中的运算符种类可以分为如下六类:
- 算术运算符
- 赋值运算符
- 比较运算符
- 布尔运算符
- 位运算符
- 其他运算符(
void
,,
)
与这些运算符紧密联系的是运算符的优先级,优先级问题是笔试与面试时经常考察的问题。
算术运算符
算术运算符中最常见的是加法运算符,但是加法运算符针对不同类型的变量,组合效果有很多种。简而言之,JS中的加法运算符可以实现数字的相加,也可以实现字符串的拼接。
// ————数字相加————
console.log(1 + 1); // 2
console.log(true + true); // 2
console.log(1 + true); // 2
console.log(false - 1); // -1
// ————字符串拼接————
console.log('1'+'1'); // '11'
console.log('1.1' + ' ' + '1.1'); // '1.1 1.1'
加法的计算方法一般如下:
如果运算元素是对象,那么会先转化为原始类型的值(即
valueOf
方法),如果其结果还不是原始类型的值,再调用toString
方法
var obj = {name: 'Lyndon', age: 22};
console.log('1' + obj); // '1[object Object]'
var obj = {
name: 'Lyndon',
toString: function toString(){
return 'Lyndon';
},
valueOf: function valueOf(){
return 'morning'
}
};
console.log(1 + obj); // '1morning'
var obj = {
name: 'Lyndon',
toString: function toString(){
return 'Lyndon';
}
};
console.log(1 + obj); // '1Lyndon'
如果对象是
Date
实例,那么先执行的是toString
方法
var day = new Date();
console.log(day); // 'Sat Dec 31 2016 19:19:11 GMT+0800 (中国标准时间)'
console.log('good' + day); // 'goodSat Dec 31 2016 19:19:11 GMT+0800 (中国标准时间)'
两个运算元素都是原始类型值,但只要有一个运算元素是字符串,那么另一个运算元素也会转换为字符串,执行字符串拼接
var a = 10, b = 'days';
console.log(a + b); // '10days'
只有加号和一个运算元素,如果唯一的运算元素是字符串,那么会转换为数字,所以结果有可能是
NaN
console.log(+ '78'); // 78
console.log(+ 'good'); // 'NaN'
否则,两个运算元素皆转换为数值,执行算术加法运算
由于加法运算极大地依赖于参数的类型,因此使用时应该首先明确到底要执行哪种运算,如果不确定最好加上小括号。如下面的例子就说明字符串在算式中的位置不同,结果也会不同。
console.log('3' + 4 + 5); // '345'
console.log(3 + 4 + '5'); // '75'
console.log(3 + '4' + 5); // '345'
特别需要注意的还有两点
- 加法运算符对于其他类型的值会自动转化为字符串,然后进行字符串的拼接。
var a = [1, 2];
var b = 3;
console.log(a instanceof(Object)); // true
console.log(a + b); // '1,23'
2 + [2]; // '22',等同于String(2) + String([2])
- 如果只有一个运算元素,必须将其放在右边
2 + ; // Uncaught SyntaxError: Unexpected token ;
console.log(2 + ); // Uncaught SyntaxError: Unexpected token )
+-6; // -6,等同于+(-6)
+ 3 + 4; // 7,等同于+(3+4)
相较加法运算符而言,剩下的算术运算符都相对简单些,其原则就是:将所有的运算元素全部转换为数值,再执行相应的算术运算。
'1'/2; // 0.5
'a'/2; // NaN
'1a'/2; // NaN,转换为数字的方式是Number('1a'),而不是parseInt('1a')
需要注意几个特殊的情况。
求余/求模运算,判断奇偶数时常用到
%
运算符,但是对于负数而言需要使用到绝对值,否则不能给出正确答案
function isOdd(n){
return n % 2 === 1;
}
console.log(isOdd(-3)); // false
console.log(isOdd(-4)); // false
但是-3
明显是一个奇数,应该返回true,所以需要进行如下修改:
function isOdd(n){
return Math.abs(n) % 2 === 1;
}
console.log(isOdd(-3)); // true
console.log(isOdd(-4)); // false
自增与自减的运算符前后问题。如果将
++
或--
放在变量之后,会先返回变量原始值,再进行自增或自减操作;如果将符号放在变量之前,会先进行自增或自减操作,再返回操作后的结果
var a = 1, b = 1;
a1 = a++;
b1 = ++b;
console.log(a1, b1, a, b); // 1 2 2 2
赋值运算符
常用的赋值运算符有:=
, +=
, -=
。关于赋值运算符的主要考虑问题,应该是运算符的优先级与左结合、右结合问题。
比较运算符
比较运算符进行运算后返回的结果始终是布尔值。
==
是相等,具体而言是:比较两个值是否相等;===
是严格相等,具体而言是:两个值是否是同一个值,需要保证变量类型也是相同的;!=
是不相等,!==
是严格不相等
2 == '2'; // true
2 === '2'; // false
1 == true; // true
1 === true; // false
NaN === NaN; // false,NaN与任何值都不想等,包括其自身
15 === 0xf; // true,同类型的基础类型值,虽然进制不一样,但是代表的值相等,因此也严格相等
除了相等与严格相等运算符之外,如果两个运算元素都是字符串,那么将按照字典顺序进行ASCII值的比较;否则两个运算元素都会转换为数值进行比较
5 > '4'; // true
false < true; // true
'cathy' > 'Cathy'; // true,因为小写字母c的ASCII值为99,而大写字母C的ASCII值为67
如果运算元素是对象,那么会先调用
valueOf
方法,如果返回的还是对象,接着调用toString
方法
var x = [];
x.valueOf = function(){
return 12;
}
console.log(x < '11'); // false
console.log(x > '11'); // true
var y = [2];
x > '12'; // false,等同于[2].valueOf().toString()='2'>'11'
var x = {a: 2, b: 3};
var y = {a: 3, b: 4};
console.log(x > y); // false
console.log(x < y); // false
console.log(x >= y); // true,等同于x.valueOf().toString()>=y.valueOf().toString(),结果是'[object Object]'>='[object Object]'
复杂类型的比较原则:两个复杂对象是否指向同一个对象
[] === []; // false
var a1 = [];
var a2 = a1;
a1 === a2; // true
相等运算符:对于基础类型的值会转换成数值类型再进行比较;如果对象与基础类型值比较,会先将对象转化为原始类型的值,再进行比较
[1] == 1; // true
[1] == '1'; // true,等同于Number(String([1]))==Number('1')
[1] == true; // true
相等运算符之后隐藏的类型转换,往往会使人困惑,因此最好广泛使用严格相等运算符,少使用相等运算符
布尔运算符
布尔运算符用于将表达式转换为布尔值。
取反运算符会自动将非布尔值转换为布尔值,以下是能够转换为
false
的5个值,对于其取反就能转换为true
。两次取反是将一个值转换为对应布尔类型的简便方法
!undefined; // true
!null; // true
!''; // true
!0; // true
!NaN; // true
!!NaN; // false
且运算符(&&):如果第一个运算元素的布尔值为
true
,那么会返回第二个运算元素的值;如果第一个运算元素的布尔值为false
,那么直接返回第一个运算元素的值。这种跳过第二个元素的机制常被称为"短路"
var a = 2;
console.log('hello') && (a - 2); // undefined,等同于console.log('hello')并不返回任何值,所以是undefined,而!!undefined=false,所以直接返回第一个运算元素的值:undefined
或运算符(||):如果第一个运算元素的布尔值为
true
,那么会返回第一个运算元素的值;如果第一个运算元素的布尔值为false
,则返回第二个运算元素的值
var a = 2;
console.log('hello') || (a - 2); // 0,等同于第一个运算元素的布尔值为false,那么返回第二个运算元素的值0
三元运算符(?:):与
if...else...
条件判断语句有着相同的表达效果,但是三元条件运算符有返回值,if...else...
却没有
console.log(0 ? true : false); // false
console.log(1 ? true : false); // true
位运算符
位运算符中比较常用的是:或运算与与运算。位运算符直接处理的是每一个比特位,是非常底层的运算,常常用在加密算法中,好处是速度比较快,缺点是不直观,增加了代码的复杂度,提升了维护成本。
或运算(or):符号为
|
,两个二进制位都为0,则结果为0,否则为1
与运算(and):符号为&
,两个二进制位都为1,结果为1,否则为0
console.log(1 | 3); // 3,等同于:1表示为二进制是001,3表示为二进制是011,因此最后结果为011,转化为十进制即为3
console.log(1 & 3); // 1,等同于:1表示为二进制是001,3表示为二进制是011,因此最后结果为001,转化为十进制即为1
位运算符仅仅对整数有效,遇到小数时,会将小数点后的部分抹去,只保留整数部分。
console.log(1 | 3.965); // 3
在JS内部,数值都是以64位浮点数的形式进行存储,但是进行位运算的时候,是以32位进行运算的。也即2在JS内部是
00000000000000000000000000000010
,因此进行否运算时,原来的0全部变为1,而1变为0。因此-2
进行否运算后第一位是1,所以这个数是一个负数。但是JS采取补码的方式来表示负数,因此需要将这个数减去1,再取一次反,所以最后的结果是00000000000000000000000000000011
,加上负号后就是-3
console.log(~2); // -3
void运算符
void运算符的作用是:执行表达式,然后只返回undefined
,由于void的优先级比较高,因此使用时最好加上小括号
console.log(void 1 + 2); // NaN,等同于:undefined + 2
console.log(void(1 + 2)); // undefined
逗号运算符
逗号运算符的作用是对两个表达式求值,然后返回后一个表达式的值。
1, 10; // 10
var x = 1;
var y = 10;
var z = (x, y);
z; // 10