深究js(五)——运算符

    JavaScript的运算符用于算术表达式、比较表达式、逻辑表达式、赋值表达式等等。大多数的运算符都是标点符号,如加减乘除,而有一些运算符是关键字,如delete和instanceof。下图是有关运算符的,顺序按优先级排列,由高到低优先级依次减少(每个水平分割线内的操作符都是同一优先级),标题A为运算符的结合性,L(从左往右),R(从右往左),标题N为操作数的个数,标题为类型的表示期望的操作数类型和运算符结果类型。


    在JavaScript的操作符中,我们可以根据操作数的个数进行分类。如果一个复杂表达式由一个操作符和一个表达式组成的话,这个运算符叫一元运算符,如求反的-x。如果两个表达式加一个操作符组成一个表达式的话,这叫二元运算符,如x+y。如果是三个表达式和一个操作符组成一个复杂表达式的话,这叫三元运算符,如a?b:c。

    在该表中有个注释叫左值,左值指的是表达式只能出现在赋值运算符的左侧,如变量、对象属性和数组元素都是左值。在ECMAScript规范里允许内置函数返回一个左值,但自定义的函数则不能返回一个左值。

    需要注意的是,在JavaScript的优先级里,属性访问表达式和调用表达式的优先级比typeof的还要高。也就是说,像如下的例子:

typeof my.functions[x](y)
    该表达式运行的方向是先执行中括号的,再执行小括号的,最后才到typeof。在JavaScript中,运算的顺序是从左往右的。接下来下面就来填补前一章所遗留下来的坑,这个坑分别是算术表达式的运算符、关系表达式的运算符、逻辑表达式的运算符合赋值表达式的运算符。


一、算术运算符

    常见的基本算术运算符有五种,分别是“+”、“-”、“*”、“/”和“%”。“+”留作一会再讲,现在讲其余四个。这四个运算符在执行操作的时候,JavaScript会自动的将操作数转换为数字类型,然后进行操作,如果转换的结果是NaN的话,则运算返回的是"NaN"。在JavaScript中,所有的数字都是浮点型,所以在执行两个整数相除,如:

var x = 5/2
    返回的是2.5而不是2。在别的编程语言如java,如果除数为0的话则会报错,但是在JavaScript中则不会报错,返回的结果是正无穷大或者负无穷大,而如果0除0的话返回结果是NaN,这个需要注意。

    操作符“%”是进行求余运算,即求两数相除的余数,比较常见的是整数之间求余,其实小数也可以求余。如6.5%2.1,余数是0.2。

    接下来着重讲的是“+”运算符。为什么放到最后讲,是因为“+”在JavaScript中有两种用法,第一种上一篇已讲过,就是字符串的连接,第二种是两数求和。其中前者的优先级是比后者高。比如:

var x = 1 + "1"
    返回的结果是11而不是2,因为有字符串1,所以执行这个运算的时候会把数字类型1转换成字符串类型,然后两个字符进行连接。也就是说,如果操作数中有一个是字符串的话,那么“+”执行的是字符串连接。除此之外,如果操作数是对象的话,则将对象转换成字符串,如果进行字符串连接。如用JavaScript内置的Date对象作为举例:

var date = new Date()
var x = 1 + date
    返回的结果是一串字符串。

    基本的运算符除了上述五种外,还有一元算术运算符,有“+”,“-”,“++”,“--”,这里的“+”和“-”与上面的不同,这里的是表明数值的正负。一元运算符具有很高的优先级,而且往往都是右结合。这四个运算符如果操作数是字符串的话会将其转换成数字类型,然后进行操作,如果转换成NaN的话则返回NaN。下面有一个误区列举出来让大家看清“++”的用法:

var a = "1"
a++
    如果你以为这个输出的结果是11的话那就大错特错了,“++”运算符不会进行字符串的连接,而是将字符串转换成数字类型然后加1,所以结果是2.。但是如果你是这样的话:

var a = "1"
a = a + 1
    则得到的是11,因为a是字符串,所以加号执行的是字符串连接。除了这些以外,我还要讲一下“++”和“--”他们的数放在左边和放右边是有什么样的区别。当操作符在操作数之前,叫前增量,如++i,这个是将操作数转换成数字类型,并加1,然后返回结果;如果操作符在操作数之后,叫后增量,如i++,这个是同上,但是不返回加1后的结果。如:

var a,b
a = 1; b = ++a
var a,b
a = 1; b = a++
    第一个返回的结果是a和b都为2,第二个返回的结果分别是2和1。同理“--”操作符的结果也是一样的。


二、位运算符

    在讲这个位运算符之前,首先要讲明白整数的二进制编码还有区分好反码和补码的概念,不然等一下会晕乎乎的。我在第二遍讲类型的时候有些地方讲的不明白,现在纠正过来。在JavaScript中是不区分整数值和浮点数值,JavaScript中的所有数字均用浮点数值来表明,JavaScript采用的是IEEE754标准定义的64位浮点格式来表示数字。但是,如果是在一些操作中,如数组索引和位操作符,则是基于32位整数的。这个我已经在第二篇那里纠正过来了,请见谅。

    在二进制中,数不是1(真)就是0(假),那么简单的1在二进制中,全写应该是这样的“0000 0000 0000 0000 0000 0000 0000 0001”,简写可以将前面的0省略,如:1。而反码就是将原来位数全部取反,如果拿1来作例子的话,则1的反码是“1111 1111 1111 1111 1111 1111 1111 1110”。而补码,通常是求一个数的负值,如1的负值是-1,则1的补码就是这个负数的值,那么补码是如何计算的呢,就拿-1来将,首先要求它绝对值的编码,-1的绝对值是1,则他的编码如上所述。然后求1的反码,1的反码上面也有写明,这里不再详写。然后对反码的第一位数加1则得到-1的二进制编码:“1111 1111 1111 1111 1111 1111 1111 1111”

    基础打好了,那么现在来讲位运算符。在JavaScript中,运算符有七种,分别是&(按位与)、|(按位或)、^(按位异或)、~(按位非)、<<(左移)、>>(带符号右移)和>>>(无符号右移)。下面一个一个的讲。

    按位与运算是对它的整型操作数逐位执行布尔与运算。只有两个操作数中相对应的位都是1,结果那一位才是1。如10 & 5,首先将10转换成二进制得:1010。5转换成二进制得101。然后逐位进行与运算。二进制第一位是右边的第一个数,所以从第一位进行比较,0和1为假得0,1和0为假得0,0和1为假得0,1和0为假得0,所以结果是0。

    按位或运算是对它的整型操作数逐位进行布尔或运算,相应位中至少有一个为1则为1,。如10 | 5。按照上面的分析可得结果是1111,转换成十进制是15。

    按位异或运算是对它的整型操作数逐位进行布尔异或运算,如果两个操作数中只有一个相应位为1则为1,反之则为0。如10 ^ 5,结果也是15。而10 ^ 6的结果是12,10 | 6的结果是14。

    按位非运算是将操作数的所有位取反。如果对一个整数使用按位非运算的话,相当于改变它的符号并减一。如~1的结果是-2而不是-1,理解这一点很重要,因为上面说过,一个数的负值二进制也就是该数的绝对值的补码,是由该数的原码求出反码,然后在反码的基础上加一得出补码的,所以~1的结果是-2。

    在正负数中,左移的方式都是一样的,就是将一个操作数中的二进制位进行左移操作,移动的位数由第二个操作数来决定的,移动的位数是0~31之间的整数。左移是二进制位整体向左移动几位,左边的数溢出后由右边的补回0,如3<<2的结果是12。因为3的二进制是11,向左移两位是1100,即12。实际上左移几位相当于第一个操作数乘以2的第二个操作数次方,第二个操作数是2,所以是3乘以2的2次方。不管正负数,如果左移了31位,如2<<31,结果都是0。

    但是在右移中,是区分符号的,这里先讲有符号右移,也就是(>>)。在右移操作中,二进制位向右移动,右边溢出的在左边补,如果这个数是正数,则左边补0,如果是负数则补1。如7>>1=3,而-7>>1=-4,这里右移相当于第一个操作数除以2的第二个操作数的次方,因为上面讲过,负数的二进制是该数绝对值的反码加1,也就是相当于改变它的符号并减一,所以-7>>1等于的是-4而不是-3。

    右移有一个不带符号的,记为(>>>),这里不管是正负数,右边溢出后在左边一律补0,也就是说-7>>>1=2147483644。有符号右移(>>)如果移31位,正数的结果是0而负数是-1。如果是无符号右移31位,则正数是0负数是1。不管是左移还是右移,如果移过了31位,则等于重头移过。如移32位等于移0位,移33位等于移1位,如此类推。


三、关系运算符

    关系运算符有分四种,分为相等和不等运算符(“==”、“===”、“!=”、“!==”)、比较运算符(“<”、“>”、“<=”、“>=”)、in运算符合instanceof运算符。下面一个一个的讲。

    相等运算符(==)和恒等运算符(===)其实有比较大的区别,在JavaScript中,操作数可以根据操作符来自动转换类型,但是在“===”运算符中,操作数则不能进行类型转换,恒等运算符(也称严格相等运算符)不仅比较值,也比较操作数的类型。特别是用在对象之间的比较,如果两个不同变量,即使里面的属性名和值都完全一样,但是在恒等运算符眼里,他们就是不等的,而在相等运算符眼里是相等的。如:

var a = {x : 1}
var b = {x : 1}
alert(a.x == b.x)
alert(a.x === b.x)
    第一个返回的是true,第二个返回的是false。如果要想恒等运算符返回的是真,则要对对象进行复制,详细的看我之前的第二篇。下面一幅图是摘自《JavaScript权威指南》的一幅图,详细的说明了哪种情况下,恒等运算符输出是真,哪种情况下输出的是假。


    而相等运算符进行比较的时候,则可以进行类型的转换,如果1 == “1”,返回结果是真,尽管两个类型不同,但是在JavaScript中已经自动的进行了类型转换,所以结果才返回真。下面同样用一幅图来说明相等运算符什么情况会返回真,什么情况返回假:



    比较运算符用来检测两个操作数的大小关系,这个大小关系有数值大小和字母表大小,这里的字母表大小指的是16位Unicode字符的索引顺序。在进行比较运算的时候,操作数的类型会自动的转换,在比较运算中,如果两个数有一个是数字,则另一个操作数会转换成数字类型。这里和“+”运算符有些区别,“+”运算符如果有一个操作数是字符串的话则另一个操作数会转换成字符串。所以在进行“11”<“3”和“11”<3的比较的时候,前者是字符串的比较,后者将字符串11转换成数字11然后比较,所以前者返回true或者返回false。如果一个操作数是NaN的时候,则比较运算符得出的结果将会是false。

    in运算符希望它的左操作数是一个字符串可以转换成字符串,希望它的右操作数是一个对象,如果右侧的对象有一个名为左操作数的属性名,则返回真。如:

var point = {x : 1, y : 1}
alert("x" in point)    //返回真,因为point对象里面有x属性
alert("z" in point)    //返回假,因为point对象没有z属性
alert("toString" in point)    //返回真,因为point对象继承了toString方法

var data = [7, 8, 9]
alert("0" in data)    //返回真,因为数组里面有下标为0的元素
alert(1 in data)      //返回真,因为将数字1转换成字符串1
alert(3 in data)      //返回假,因为该数组下标最大为2
    instanceof运算符则希望左操作数是一个对象,右操作数标识对象的类。如果左侧的对象是右侧类的实例,则返回真,否则返回假。需要注意的是,这个判断是会包含父类来进行判断的,比如Object类是所有对象的实例。下面有一个例子;

var a = new Date()
alert(a instanceof Date)      //返回真,因为a就是由Date类来实例的
alert(a instanceof Object)    //返回真,因为所以对象都是Object类的实例
alert(a instanceof Number)    //返回假,对象a不是Number的实例

var b = [1, 2, 3]
alert(b instanceof Array)     //返回真,因为b是Array类的实例
alert(b instanceof Object)    //返回真,因为数组是特殊的对象
alert(b instanceof RegExp)    //返回假,因为数组并不是正则类的实例


四、逻辑运算符

    逻辑运算符有如下三种:“&&”、“||”和“!”,这些操作符是对操作数进行布尔算术运算,经常和关系运算符一起配合使用的。首先讲的是“&&”。“&&”执行的是布尔与操作,这个“&&”有时候也称短路,因为这个运算符一定要两个操作数都是真值才返回真,否则则返回假。所以,如果是第一个操作数返回的是假,则不会执行到第二个操作数,直接返回假。如果第一个操作数返回的是真,则要考虑第二个操作数是否是真值,如果为假则返回假,真则为真。所以灵活的运用“&&”操作符将会减少不必要的代码。

    “||”运算符执行的是布尔或操作,操作数中只要有一个为真则返回真。在操作中,如果第一个为真的话则不会执行第二个操作数,否则则会执行第二个操作数,结果由第二个操作数来决定,这样可以用来写一些比较复杂的表达式,如:

var a = b || 10    //如果有这个变量ba=b,否则a=10
    “!”运算符首先是将操作数转换成布尔值,然后再对布尔值求反。所以“!”运算符总是返回的是真或假。


五、赋值运算符

    常用的赋值运算符是“=”,“=”的运算顺序是从右到左,也就是说像x=y=z=0的意思是给x、y、z三个变量初始化为0。除了这些赋值运算符之外,还有“+=”、“-=”、“*=”、“/=”、“%=”、“<<=”、“>>=”、“>>>=”、“&=”、“|=”和“^=”,这些都不细讲,因为多少人都知道什么意思。如:a+=1即为a=a+1。


六、逗号运算符

    逗号运算符是二元运算符,它的操作数可以是任意类型。它首先计算左操作数,然后计算右操作数,最后返回右操作数的值。它总是会计算左侧的表达式,但计算结果忽略掉,返回的是右操作数的值。如:

i = 0, j = 1, k = 2
    在浏览器的开发者工具栏里输入这段代码,返回的结果是2。其实这个代码等价于下面这段代码;

i = 0; j = 1; k = 2
    所以当左侧的表达式有副作用的时候,但是不想要它的结果,可以用这个逗号运算符。逗号运算符用的最多的是在for循环体里。


七、其他运算符

    其他常见的运算符有条件运算符(?:)、typeof运算符、delete运算符合void运算符。下面则逐个讲解。

    条件运算符是一个三元运算符,如果第一个操作数为真,则取第二个操作数的值作为它的值,否则取第三个操作数的值。如:

a > 0 ? 2 : -2
    typeof运算符是放在操作数前面,用来返回操作数的类型,返回结果是个字符串,下表是经过typeof运算返回的值:

    typeof运算符可以写成typeof(),括号内填参数,使得这个运算符看起来像函数。

    delete运算符是用来删除对象属性或者数组元素,在删除一个对象的属性或者一个数组的元素时,这个属性或者元素不仅仅被设置成一个underfined值,当删除属性,这个属性将不再存在,而删除数组的元素,虽然该元素被删掉,但是数组长度不变。不过不管怎么样,用in运算符是找不到他们的存在。并不是任何东西都可以通过delete运算符删除,一些内置核心和客户端属性是不能删除,用var声明的变量是不能删除,通过function声明的函数和函数参数也是不能删除。在严格模式中,如果delete运算符操作非法,则抛出语法错误,在非严格模式中,如果delete运算符操作非法,则返回false。

    void运算符是一元运算符,它在操作数之前,它的运算过程是操作数会照常运算,但是返回的结果是忽略操作数的运算结果并返回underfined,也就是说会忽略操作数的值。void运算符更多用在客户端URL,如:

<a href="javascript:void window.open()">a</a>
    这个void则让浏览器不必显示这个表达式的计算结果。但是这个void表达式如今比较少用。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值