JS中的运算符

JavaScript中的运算符易错点

ES5的运算中,要理解和记忆运算这方面的知识有点小繁杂,如:在一些情况下,JS会自动把非数值转化为数值,或是把数值转化为非数值,或是不转化。

算术符

JavaScript允许非数值的相加

//布尔会自动转化为字符
1+true//2;
//字符串相加为字符串连接起来
'a'+'bcd'//'abcd';

一个运算子为字符串,一个为非字符串,先把非字符串转化为字符串再按上一行的做法做加法

1 + 'a' // "1a"
false + 'a' // "falsea"

不同的运算顺序对结果也会有影响

//只有加法运算会发生以下此类重载,减法、乘法、除法不会(它们的做法是将所有的子运算转化为数字再做运算)
'3' + 4 + 5 // "345" 
3 + 4 + '5' // "75" 运算顺序由左边的3+4开始,再将所得结果与‘5’字符串相加

JavaScript允许对象的相加。先将对象转化原始类型的值,再与其他数相加。如下例子中object的原始类型为[object Object]。这个转化过程有一定的规则:首先,自动调用对象的valueOf方法,这时总是返回对象自身,这时再自动调用对象的toString方法,将其转为字符串。

var object={a:6};
object+1  //"[object Object]1"

var obj = { a: 1 };
obj.valueOf() // { p: 1 }  返回了对象自身

var obj = { p: 1 };
obj.valueOf().toString() // "[object Object]" 再返回对象自身的基础上转化为其字符串

此外,valueOf()方法和toString()方法可以自定义,如下:

var object={
valueOf:function(){return 1;}
}
object+2=3;

var object={
toString:function(){return "Hello";}
}
object+2="Hello2";

Date类型在这此为特例。它的转化过程为先调用toString方法再调用valueOf方法。如下所示:

var obj = new Date();
obj.valueOf = function () { return 1 };
obj.toString = function () { return 'hello' };

obj + 2 // "hello2"

余数运算符:在浮点运算中应用较少。其结果取决于第一个运算子。为了得到负数的正确余数的话,不能直接使用该运算符。

function isOdd(n) {
 return Math.abs(n % 2) === 1;
}
isOdd(-5) // true
isOdd(-4) // false

自增、自减运算与C++等编程语言中完全相同。

var x = 1;
++x // 2
x // 2

--x // 1
x // 1

数值运算符(+)、负数值运算符(-)都有将非数值转化为数值的作用。

+true // 1
+[] // 0
+{} // NaN

指数运算符(运算顺序从右往左)

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

赋值运算符:与C++等编程语言中常见用法完全相同。该运算符可以与其他算术运算符或位运算符结合使用。格式为:a X =b;(X为加或减,乘或除,取余或取指数,左移或右移,X也可以是&、|、^)

比较运算符

一、非相等运算符:字符串的比较 字符串按照字典顺序进行比较

'cat' > 'dog' // false
'cat' > 'catalog' // false
'cat' > 'Cat' // true'
'大' > '小' // false  由于所有字符都有 Unicode 码点,因此汉字也可以比较。

二、非相等运算符:非字符串的比较

(1)原始类型值 如果两个运算子都是原始类型的值,先转成数值再比较

true>0;//true bool值转化为数值
15<'16'//true 字符串转化为数值
true>false;//true  bool值转化为数值

注意任何值与NaN比较都是false(NaN与NaN比较也是)

NaN<1;//false
NaN>=1;//false
NaN<=NaN;//false
NaN<'1'//false

(2)对象

如果运算子是对象,会转为原始类型的值,再进行比较
对象转换成原始类型的值,算法是先调用valueOf方法;如果返回的还是对象,再接着调用toString方法

①一个运算子为对象的实例如下:

var a = [3];
a>'11';//true 此处的比较的是3与1的比较
a.valueOf = function () { return '3' };
a > '11' // false
// 等同于 [3].valueOf() > '11'
// 即 '3' > '11'

②两个运算子为对象的实例如下:

[2] > [1] // true
// 等同于 [2].valueOf().toString() > [1].valueOf().toString()
// 即 '2' > '1'

[2] > [11] // true
// 等同于 [2].valueOf().toString() > [11].valueOf().toString()
// 即 '2' > '11'

{ x: 2 } >= { x: 1 } // true
// 等同于 { x: 2 }.valueOf().toString() >= { x: 1 }.valueOf().toString()
// 即 '[object Object]' >= '[object Object]'

三、严格相等运算符

(1)不同类型的值,一定返回false;
(2)同一类的原始类型值。只要值也相等就返回true。反例如下:

NaN === NaN  // false

易出错的有下面这种情况。十进制和十六进制的数类型其实是相同的。

1 === 0x1 // true

(3)复合类型值
两个复合类型(对象、数组、函数)的数据比较时,不是比较它们的值是否相等,而是比较它们是否指向同一个地址。因此,比较两个空对象、两个空数组、两个空函数,结果都是不相等。

{} === {} // false 对象不完全相等
[] === [] // false数组不完全相等
(function () {} === function () {}) // false函数不完全相等

指向同一个地址的严格相等的情况如下:

var a1={};
var a2=a1;
a2===a1;//true

对于对象的大于或小于的比较,比较的值,而不是地址。

var object1={};
var object2={};
object1<objecr2;//false
object1===objecr2;//false

(4)undefined 和 null
undefined和null与自身严格相等。

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

var v1;
var v2;
v1===v2;//true

四、相等运算符,有一下四种情况:

(1)原始类型值原始类型的值会转换成数值再进行比较
其中bool转化为0或1,含字母的字符串转化为NaN,空字符转化为0。

1 == true // true
// 等同于 1 === Number(true)

0 == false // true
// 等同于 0 === Number(false)

2 == true // false
// 等同于 2 === Number(true)

2 == false // false
// 等同于 2 === Number(false)

'true' == true // false
// 等同于 Number('true') === Number(true)
// 等同于 NaN === 1

'' == 0 // true
// 等同于 Number('') === 0
// 等同于 0 === 0

'' == false  // true
// 等同于 Number('') === Number(false)
// 等同于 0 === 0

'1' == true  // true
// 等同于 Number('1') === Number(true)
// 等同于 1 === 1

'\n  123  \t' == 123 // true
// 因为字符串转为数字时,省略前置和后置的空格

(2)对象与原始类型值比较

①对象与数值将对象转化为数值

②对象与字符串将对象转化为字符串

③对象与Bool类型将对象转化为数值

// 对象与数值比较时,对象转为数值
[1] == 1 // true
// 等同于 Number([1]) == 1

// 对象与字符串比较时,对象转为字符串
[1] == '1' // true
// 等同于 String([1]) == '1'
[1, 2] == '1,2' // true
// 等同于 String([1, 2]) == '1,2'

// 对象与布尔值比较时,两边都转为数值
[1] == true // true
// 等同于 Number([1]) == Number(true)
[2] == true // false
// 等同于 Number([2]) == Number(true)

(3)undefined 和 null

undefined和null与其他类型的值比较时,结果都为false,它们互相比较时结果为true

五、不相等运算符

先求得相等运算的结果再返回其相反数即可。

布尔运算符

一、取反运算符(!)

以下六个值取反后得到true,其余得到false.
undefined NaN ' ' false null 0

以下做法与Boolean函数相同作用:

var a =1;
!!a;//将a转化为bool类型

二、且运算符(&&)

与C++等编程语言中的“且”的规则不太一样:

(1)左运算子为false.返回第一个运算子的值(而不是bool值),且完全不理会第二个运算子。这种跳过第二个运算子的机制,被称为“短路”。

var x = 1;
(1 - 1) && ( x += 1) // 0
x // 1

(2)左运算子为true.返回第二个运算子的值(而不是bool值)。

(3)有多个运算子的运算.返回第一个bool值为false的运算子的值,若不存在这样的运算子,则返回最后一个运算子的值

三、或运算符(||)

与&&的运算类似。

(1)左运算子为false.返回第二个运算子的值(而不是bool值)。

(2)左运算子为true.返回第一个运算子的值(而不是bool值),且完全不理会第二个运算子。此处发生了“短路”。

var x = 1;
true || (x = 2) // true
x // 1

(3)有多个运算子的运算.返回第一个bool值为true的运算子的值,若不存在这样的运算子,则返回最后一个运算子的值

四、三元运算符(?:)

与if…else语句类似,但略有不同:
if…else是语句,没有返回值;三元条件表达式是表达式,具有返回值。

二进制运算符

二进制运算是32位带符号的整数间的运算。如果运算子不是32位带符号的整数,则自动将其转化为32位带符号的整数。
利用这一特性,可以写如下几个将任意数转化为32位带符号整数的函数:

function v1(x)
{return x|0;}

function v2(x)
{return x<<0;}

function v3(x)
{return x^0;}

function v4(x)
{return (x^1)^1;}

function v5(x)
{return ~~x;}

function v6(x)
{return x>>0;}

一、二进制或运算符(or)符号为|,表示若两个二进制位都为0,则结果为0,否则为1。

0 | 3 // 3

二、二进制与运算符(and)符号为&,表示若两个二进制位都为1,则结果为1,否则为0。

0 & 3 // 0

三、二进制否运算符(not)符号为~,表示对一个二进制位取反。

①对Number类型的取反:先将一个数写成有符号32位二进制的形式,再将每一位取反,再将该有符号32位二进制转化为整数。

var a=~5
-6

5的32位二进制为:0000 0000 0000 0000 0000 0000 0000 0101
每一位取反之后得1111 1111 1111 1111 1111 1111 1111 1010
该数减一取反后为:0000 0000 0000 0000 0000 0000 0000 1001 ,为6。因此~5为-6。
可以简单地记为:一个数与自身的取反相加为-1.

②对字符串类型的取反:先将字符串转化为数值。

~'12'//-13
~'0032'//-33
~'Hello'//-1
~'33www'//-1
~'0xFFFFFFF0'//16

③对其他类型的取反:先将该类型转化为数值。

// 相当于 ~Number([])
~[] // -1

// 相当于 ~Number(NaN)
~NaN // -1

// 相当于 ~Number(null)
~null // -1

注意将前三种位运算与布尔运算中的取反(!)、且(||)、或(&&)区别开来。

四、异或运算符(xor)符号为^,表示若两个二进制位不相同,则结果为1,否则为0。

交换两个数的值的过程如下:

var a = 10;
var b = 99;

a ^= b, b ^= a, a ^= b;

a // 99
b // 10

这种方法的好处是不需要另外设临时变量。
五、左移运算符(left shift)符号为<<
向左移,在尾部补零。

4 << 1
// 8

-4 << 1
// -8  

将颜色的 RGB 值转为 HEX 值如下:

var color = {r: 186, g: 218, b: 85};

// RGB to HEX
// (1 << 24)的作用为保证结果是6位数
var rgb2hex = function(r, g, b) {
  return '#' + ((1 << 24) + (r << 16) + (g << 8) + b)
    .toString(16) // 先转成十六进制,然后返回字符串
    .substr(1);   // 去除字符串的最高位,返回后面六个字符串
}

rgb2hex(color.r, color.g, color.b)
// "#bada55"

六、右移运算符(right shift)符号为>>

如果是正数,头部全部补0;如果是负数,头部全部补1。右移运算符基本上相当于除以2的指定次方(最高位即符号位参与移动)。

4>>1//2
-4>>2//-1
-4>>1//-2
32>>4//2
21>>2//5
21.5>>2//5

七、头部补零的右移运算符(zero filled right shift)符号为>>>

对于正数,头部补零与不补零的右移运算结果一样。对于负数,由于一个补的是零一个补的是1,结果很不一样。

4 >>> 1
// 2

-4 >>> 1
// 2147483646
/*
// 因为-4的二进制形式为11111111111111111111111111111100,
// 带符号位的右移一位,得到01111111111111111111111111111110,
// 即为十进制的2147483646。
*/

查看一个负整数在计算机内部的储存形式,最快的方法就是使用这个运算符。

-1 >>> 0 // 4294967295

其他运算符、运算顺序

一、void运算符
执行一个表达式,并返回undefined
因为void运算符的优先性很高,如果不使用括号,容易造成错误的结果。
用户点击链接提交表单,但是不产生页面跳转,如下:

<a href="javascript: void(document.form.submit())">
 提交
</a>

二、逗号运算符
逗号运算符用于对两个表达式求值,并返回后一个表达式的值。
逗号运算符的一个用途是,在返回一个值之前,进行一些辅助操作。

var value = (console.log('Hi!'), true);
// Hi!

value // true

三、运算顺序

①优先级
五个运算符的优先级从高到低依次为:小于等于(<=)严格相等(===)或(||)三元(?:)等号(=)
②圆括号的作用
作用有二:一是把表达式放在圆括号之中,提升运算的优先级;另一是跟在函数的后面,作用是调用函数。
注意 :因为圆括号不是运算符,所以不具有求值作用,只改变运算的优先级。

var x = 1;
(x) = 2;
//如果圆括号具有求值作用,那么就会变成1 = 2,这是会报错了。
//但是,上面的代码可以运行,这验证了圆括号只改变优先级,不会求值。

圆括号之中,只能放置表达式,如果将语句放在圆括号之中,就会报错。

(var a = 1)
// SyntaxError: Unexpected token var

③左结合和右结合
绝大部分运算都是左结合,以下为三种右结合的特例:

a=b=c=d;//a=(b=(c=d))
a=b?c:d?e:f?g:h;//b?c:(d?e:(f?g:h))
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值