操作符(运算符)
算数运算符
除了加法运算符,其他算术运算符都不会发生重载。所有操作数一律转为数值,再进行相应的数学运算。
加法运算符(+)
基本规则
操作数中没有字符串时,执行相加。
如果其中一个操作数为字符串,则将另一个操作数转换为字符串再对两个字符串做拼接。
console.log(1 + undefined); //NaN
console.log(1 + null); //1
console.log('1' + undefined); //'1undefined'
console.log('1' + null); //'1null'
加法运算符是在运行时决定执行相加还是执行拼接。所以操作数的不同会导致不同的语法行为,这种现象称为重载(overload)。
console.log(1 + 2 + 3); //6
console.log('1' + 2 + 3); //'123'
console.log(1 + 2 + '3'); //'33'
上面代码中,因为运算顺序为从左向右,所以字符串的位置不同,导致了不同的结果。
对象相加
操作数为对象,必须先转成原始类型的值,再进行相加。
对象转换为原始类型的值,首先调用valueOf
方法,一般返回对象本身,然后自动调用toString
方法,将其转为字符串。对象的toString
方法默认返回[object Object]
。
console.log({p: 1} + 1); //'[object Object]1'
console.log({p: 1} + {p: 1}); //'[object Object][object Object]'
数组的toString
方法默认返回由数组中每个值的字符串形式拼接而成的一个以逗号分隔的字符串。
console.log([1,2,3]+1); //'1,2,31'
特例。Date
对象的实例,会优先执行toString
方法(默认为返回当前时区的时间的字符串表示)
var obj = new Date();
obj.valueOf = function () {
return 1;
};
obj.toString = function () {
return 'hello'
};
console.log(obj + 2); //'hello2'
减法运算符(-)
乘法运算符(*)
除法运算符(/)
求模(取余)运算符(%)
返回前一个操作数除以后一个操作数后所得的余数。
任何数和0取余,返回NaN
console.log(12 % 5); //2
console.log(12 % 0); //NaN
运算结果的正负号由第一个操作数的正负号决定
console.log(12 % 5); //2
console.log(-12 % 5); //-2
console.log(12 % -5); //2
console.log(-12 % -5); //-2
只能操作一个值的操作符叫做一元操作符,分为:前置型和后置型。
前置型位于操作变量之前,后置型位于操作变量之后。
自增和自减运算符
一元运算符,只需要一个操作变量。
自增或自减运算符前置,变量会先进行自增/自减操作,再返回变量操作后的值;自增或自减运算符后置,会先返回变量操作前的值,再进行自增/自减操作。
var a = 1;
var b = 1;
console.log(++a); //2
console.log(b++); //1
练习
var a = 1; var b = ++a + ++a; console.log(b); //5
var a = 1; var b = a++ + ++a; console.log(b); //4
var a = 1; var b = a++ + a++; console.log(b); //3
var a = 1; var b = ++a + a++; console.log(b); //4
数值运算符(正负号)
数值运算符(+
)是一元运算符。
可以将任何值转为数值。
console.log(+true); //1
console.log(+[]); //0
console.log(+{}); //NaN
负数值运算符(-
)得到的值正负相反,连用两个负数值运算符(带括号,否则会变成自减运算符),等同于数值运算符。
var x = 1;
console.log(-x); //-1
console.log(-(-x)); //1
指数运算符
指数运算符(**
)完成指数运算,第一个操作数是底数,第二个操作数是指数。
console.log(2 ** 3); //8
逻辑运算符 (&& || !)
与(&&)
两个操作数都为true,结果为true。
只要有一个操作数为false,结果为false。
或(||)
只要有一个操作数为true,结果为true。
两个操作数都为false,结果为false。
非(!)
逻辑非操作符首先会将它的操作数转换为一个布尔值,然后再对其求反。
短路操作
如果第一个操作数能够决定结果,那么就不会再对第二个操作数求值。
&&和||都有短路操作。
- &&:如果第一个操作数的布尔值为true,则返回第二个操作数的值(不是布尔值,是值);如果第一个操作数的布尔值为false,则直接返回第一个操作数的值,且不再对第二个操作数求值。
- || :如果第一个操作数的布尔值是true,则直接返回第一个操作数的值,且不再对第二个操作数求值;如果第一个操作数的布尔值是false,则返回第二个操作数的值。
比较运算符
< <= >= >
- 在比较字符串时,实际比较的是两个字符串中对应位置的每个字符的字符编码值。
- 在比较数值和字符串时,字符串都会被转换成数值,然后再以数值方式与另一个数值比较。
- 如果是非字符串的原始值,会先转成数值再比较。
console.log(true > false); //true
console.log(2 > true); //true
- 任何值(包括
NaN
本身)与NaN
比较,返回的都是false
- 对象进行比较,会先转为原始类型的值,再进行比较。(先调用
valueOf
方法,如果返回的还是对象,再接着调用toString
方法)
console.log({x: 2} > {x: 3}); //false
console.log({x: 2} < {x: 3}); //false
console.log({x: 2} <= {x: 3}); //true
console.log({x: 2} >= {x: 3}); //true
//对象的valueOf返回原值,toString返回'[object Object]'
console.log([2] > [11]); //true
//数组的valueOf返回原值,toString返回数组元素用逗号连接并加引号。所以'2'>'11'是正确的
- Date对象实例用于比较时,先调用
toString
方法。如果返回的不是原始类型值,再调用valueOf
方法。
== != === !==
== != 相等和不相等。
原始类型的数据会先转换成数值类型再进行比较。
console.log('true' == true);
//NaN == 1 ,false
console.log('\n 123 \t' == 123);
//字符串转为数字时,省略前置和后置的空格,true
undefined
和null
与其他类型的值比较时,结果都为false
,相互比较时结果为true
console.log(null == false); //fasle
console.log(undefined == false); //false
console.log(0 == null); //false
console.log(0 == undefined); //false
console.log(null == undefined); //true
=== !== 全等和不全等。
相等只比较值,全等比较的是值和类型。
几个特殊值的比较
console.log(1 === 0x1);
//十进制的1和十六进制的1,类型与值都相同,true
console.log(NaN === NaN);
//NaN与任何值都不相等,包括它本身,false
console.log(+0 === -0);
//正0等于负0,true
赋值运算符
= += -= *= /= %=
var num = 0;
num += 5; //相当于num = num + 5;
位运算符
参考博客 位运算符
其他运算符
void运算符
执行一个表达式,不返回任何值,或者说返回undefined
console.log(void 0); //undefined
console.log(void (0)); //undefined
上面代码的写法都正确,建议加括号的写法。因为void
运算符的优先性很高。
console.log(void 4 + 7); //NaN,相当于(void 4) + 7
console.log(void (4 + 7)); //undefined
var x = 1;
console.log(x = 5); //5
var x = 1;
console.log(void(x = 5)); //undefined
次运算符的主要用途是在a链接中插入代码防止网页跳转。
<a href="http://example.com" onclick="f();return false;">点击</a>
<script>
function f() {
console.log('hello world');
}
</script>
上面代码中,点击链接后,会先执行onclick
的代码,因为onclick
返回false
,所以浏览器不会跳转到example.com
用void
可以这样写
<a href="javascript:void (f());">点击</a>
用户点击链接提交表单,不产生页面跳转
<a href="javascript:void (document.form.submit());">提交</a>
逗号运算符
用于两个表达式求值,并返回后一个表达式的值。
var x = ('a','b');
console.log(x); //'b'
运算顺序
运算符的优先级
优先级从高到低:
- () 优先级最高
- 一元运算符 ++ – !
- 关系运算符 > >= < <=
- 逻辑运算符 先&& 后||
console.log(((4 >= 6) || ("人" != "狗")) && !(((12 * 2) == 144) && true)); //true
// ( false || true ) && !(( 24 == 144) && true)
// true && !( false && true)
// true && !false
// true && true
//true
圆括号的作用
优先级最高。圆括号中只能防止表达式,如果将语句防止圆括号之中,就会报错。
(var x = 1) //SyntaxError: Unexpected token var
左结合和右结合
优先级别相同的运算符,大多数情况,计算顺序总是从左到右,这叫做运算符的“左结合”。
x + y + z
上面带中先计算最左边x + y
,再计算+ z
少数运算符的计算顺序是从右到左,叫做“右结合”。例如赋值运算符和三元条件运算符。