javascript,六运算符

运算符概述

JavaScript中的运算符用于算术表达式、 比较表达式、 逻辑表达式、 赋值表达式等。需要注意的是, 大多数运算符都是由标点符号表示的, 比如 "+" 和"=" 。 而另外一些运算符则是由关键字表示的, 比如delete和instanceof。 关键字运算符和标点符号所表示的运算符一样都是正规的运算符。

算术运算符

赋值 y = 5, 以下表格将向你说明算术运算符的使用:

算符描述例子y 值x 值
+加法x = y + 2y = 5x = 7
-减法x = y - 2y = 5x = 3
*乘法x = y * 2y = 5x = 10
/除法x = y / 2y = 5x = 2.5
%余数x = y % 2y = 5x = 1
++自增x = ++yy = 6x = 6
x = y++y = 6x = 5
--自减x = --yy = 4x = 4
x = y--y = 4x = 5

赋值运算符

给定 x=10y=5,下面的表格解释了赋值运算符:

运算符例子实例x 值
=x = yx = yx = 5
+=x += yx = x + yx = 15
-=x -= yx = x - yx = 5
*=x *= yx = x * yx = 50
/=x /= yx = x / yx = 2
%=x %= yx = x % yx = 0

字符串运算符

+ 运算符, += 运算符可用于连接字符串。

给定 text1 = "Good ", text2 = "Morning", 及 text3 = "", 下面的表格解释了字符串运算符的使用:

运算符例子text1text2text3
+text3 = text1 + text2"Good ""Morning""Good Morning"
+=text1 += text2"Good Morning""Morning"""

比较运算符

比较运算符用于逻辑语句的判断,从而确定给定的两个值或变量是否相等。

给定 x=5, 下表展示了比较运算符的使用:

运算符描述比较结果
==等于x == 8false
x == 5true
===值及类型均相等(恒等于)x === "5"false
x === 5true
!=不等于x != 8true
!==值与类型均不等(不恒等于)x !== "5"true
x !== 5false
>大于x > 8false
<小于x < 8true
>=大于或等于x >= 8false
<=小于或等于x <= 8true

条件运算符

条件运算符用于基于条件的赋值运算。

给定 x=6 and y=3, 下表演示了条件运算符的运算:

语法例子
变量 = (条件) ? 值1:值2voteable = (age < 18) ? "太年轻而不能":"年龄够";

逻辑运算符

逻辑运算符用来确定变量或值之间的逻辑关系。

给定 x=6 and y=3, 以下实例演示了逻辑运算符的使用:

运算符描述例子
&&(x < 10 && y > 1) 为 true
||(x == 5 || y == 5) 为 false
!!(x == y) 为 true

JavaScript 位运算符

位运算符工作于32位的数字上。任何数字操作都将转换为32位。结果会转换为 JavaScript 数字。

运算符描述例子类似于结果十进制
&ANDx = 5 & 10101 & 000100011
|ORx = 5 | 10101 | 000101015
~取反x = ~ 5~01011010-6
^异或x = 5 ^ 10101 ^ 000101004
<<左移x = 5 << 10101 << 1101010
>>右移x = 5 >> 10101 >> 100102

操作数的个数

运算符可以根据其操作数的个数进行分类。 JavaScript中的大多数运算符(比如 "*" 乘法运算符)是一个二元运算符 (binary operator) , 将两个表达式合并成一个表达式。它们的操作数均是两个。 JavaScript同样支持一些一元运算符 (unary operator) 。 表达式-x中的 "-" 运算符就是一个一元运算符, 是将操作数x求负值。 JavaScript还支持三元运算符(ternary operator) , 条件判断运算符 "?:"它将三个表达式合并成一个表达式。

操作数类型和结果类型

一些运算符可以作用于任何数据类型, 但仍然希望它们的操作数是指定类型的数据, 井且大多数运算符返回(或计算出)一个特定类型的值。 JavaScript运算符通常会根据需要对操作数进行类型转换。 乘法运算符"*" 希望操作数为数字, 但表达式"3"*"5"却是合法的, 因为JavaScript会将操作数转换为数字。 这个表达式的值是数字15, 而不是字符串 "15" 。 JavaScript 中的所有值不是真值就是假值, 因此对于那些希望操作数是布尔类型的操作符来说, 它们的操作数可以是任意类型。

有一些运算符对操作数类型有着不同程度的依赖。最明显的例子是加法运算符,"+" 运算符可以对数字进行加法运算, 也可以对字符串作连接。 同样, 比如“<”比较运算符可以根据操作数类型的不同对数字进行大小值的比较, 也可以比较字符在字母表中的次序先后,单个运算符的描述充分解释了它们对类型有着怎样的依赖以及对操作数进行怎样的类型转换。

左值

你可能会注意到, 赋值运算符和其他少数运算符期望它们的操作数是lval类型。 左值 (lvalue) 是一个古老的术语, 它是指 ”表达式只能出现在赋值运算符的左侧" 。在JavaScript中, 变量、 对象属性和数组元素均是左值。 ECMAScript规范允许内置函数返回一个左值, 但自定义的函数则不能返回左值。

运算符的副作用

计算一个简单的表达式(比如2*3) 不会对程序的运行状态造成任何影响, 程序后续执行的计算也不会受到该计算的影响。 而有一些表达式则具有很多副作用, 前后的表达式运算会相互影响。 赋值运算符是最明显的一个例子:

如果给一个变量或属性赋值, 那么那些使用这个变最或属性的表达式的值都会发生改变。 "++" 和"--" 递增和递减运算符与此类似,因为它们包含隐式的赋值。

delete运算符同样有副作用:删除一个属性就像(但不完全一样)给这个属性赋值undefined。

其他的JavaScript运算符都没有副作用,但函数调用表达式和对象创建表达式有些特别,在函数体或者构造函数内部运用了这些运算符并产生了副作用的时候,我们说函数调用表达式和对象创建表达式是有副作用的。

运算符优先级

运算符是按照优先级从高到低排序的,每个水平分割线内的一组运算符具有相同的优先级。 运算符优先级控制着运算符的执行顺序。 优先级高的运算符的执行总是先于优先级低的运算符。

看一下下面这个表达式:

w = x + y * z; 

乘法运算符 "*" 比加法运算符 "+"具有更高的优先级,所以乘法先执行,加法后执行。 然后,由于赋值运算符 "=" 具有最低的优先级,因此赋值操作是在右侧的表达式计算出结果后进行的。

运算符的优先级可以通过显式使用圆括号来重写。 为了让加法先执行,乘法后执行,可以这样写:

w = (x + y)*z;

需要注意的是,属性访问表达式和调用表达式的优先级要更高。 如下:

typeof my.functions[x](y)

尽管typeof是优先级最高的运算符之一,但typeof也是在两次属性访问和函数调用之后执行的。

实际上,如果你真的不确定你所使用的运算符的优先级,最简单的方法就是使用圆括号来强行指定运算次序。 有些重要规则需要熟记:乘法和除法的优先级高于加法和减法,赋值运算的优先级非常低,通常总是最后执行的。

运算符的结合性

例如,减法运算符具有从左至右的结合性,因此:

w = x - y - Z;

和这段代码一模一样

w = ((x - y) - z);

反过来,下面这个表达式:

x = ~-y;
w = x = y = z;
q = a?b:c?d:e?f:g;

和这段代码一模一样:

x = ~(-y); 
w = (x = (y = z)); 
q = a?b:(c?d:(e?f:g)); 

因为一元操作符、赋值和三元条件运算符都具有从右至左的结合性。

运算顺序

运算符的优先级和结合性规定了它们在复杂的表达式中的运算顺序, 但并没有规定子表达式的计算过程中的运算顺序。JavaScript总是严格按照从左至右的顺序来计算表达式。例如,在表达式w=x+y*z中,将首先计算子表达式W, 然后计算x、y和Z, 然后,y的值和z 的值相乘, 再加上x的值, 最后将其赋值给表达式w所指代的变址或属性。 给表达式添加圆括号将会改变乘法、加法和赋值运算的关系,但从左至右的顺序是不会改变的。

只有在任何一个表达式具有副作用而影响到其他表达式的时候,其求值顺序才会和看上去有所不同。 如果表达式x中的一个变量自增1, 这个变最在表达式z中使用,那么实际上是先计算出了x的值再计算z的值,这一点非常重要。


算术表达式

算术计算的运算符, 以及对操作数的算术操作。乘法、除法和减法运算符非常简单。 加法不单是数字的加法同样可以做字符串连接操作,并且其类型转换有些特殊。

基本的算术运算符是*(乘法)、/(除法)、%(求余)、+(加法)和 -(减法)。 所有那些无法转换为数字的操作数都转换为NaN值。 如果操作数(或者转换结果)是NaN值,算术运算的结果也是NaN。

运算符"/" 用第二个操作数来除第一个操作数,如果你使用过那些区分整型和浮点型数字的编程语言,那么当用一个整数除以另一个整数时,则希望得到的结果也是整数。 但在JavaScript中,所有的数字都是浮点型的,除法运算的结果也是浮点型,比如,5/2的 结果是2.5, 而不是2。 除数为0的运算结果为正无穷大或负无穷大,而0/0的结果是NaN, 所有这些运算均不会报错。

运算符 "%"计算的是第一个操作数对第二个操作数的模。换句话说,就是第一个操作数除以第二个操作数的余数。结果的符号和第一个操作数(被除数)的符号保持一 致。

例如,5%2结果是1, -5%2的结果是 -1。

求余运算符的操作数通常都是整数,但也适用于浮点数,比如,6.5%2.1结果是0.2(0.19999999999999973)。

"+"运算符

二元加法运算符 "+" 可以对两个数字做加法, 也可以做字符串连接操作:

1 + 2                   // => 3
"hello"+" "+ "there"    // =>"hello there"
"1" + "2"               // => "12"

当两个操作数都是数字或都是字符串的时候,计算结果是显而易见的。 然而对于其他情况来说,则要进行一些必要的类型转换,并且运算符的行为依赖于类型转换的结果。加号的转换规则优先考虑字符串连接,如果其中一个操作数是字符串或者转换为字符串的对象,另外一个操作数将会转换为字符串, 加法将进行字符串的连接操作。 如果两个操作数都不是类字符串(string-like)的,那么都将进行算术加法运算。

从技术上讲,加法操作符的行为表现为:

如果其中一个操作数是对象,则对象会遵循对象到原始值的转换规则转换为原始类值: 日期对象通过toString()方法执行转换,其他对象则通过valueOf()方法执行转换(如果valueOf()方法返回一个原始值的话)。 由于多数对象都不具备可用的valueOf()方法, 因此它们会通过toString()方法来执行转换。

在进行了对象到原始值的转换后, 如果其中一个操作数是字符串的话, 另一个操作数也会转换为字符串,然后进行字符串连接。

否则, 两个操作数都将转换为数字(或者NaN) , 然后进行加法操作。

这里有一些例子:

1 + 2           // => 3: 加法
"1" + "2"       // => "12": 字符串连接
"1" + 2         // => "12": 数字转换为字符串后进行字符串连接
"1" + {} // => "1[object Object]": 对象转换为字符串后进行字符串连接 true + true // => 2: 布尔值转换为数字后做加法 2 + null // => 2: null转换为0后做加法 2 + undefined // =>NaN: undefined转换为NaN后做加法

最后, 需要特别注意的是, 当加号运算符和字符串和数字一起使用时, 需要考虑加法的结合性的对运算顺序的影响。 也就是说, 运算结果是依赖于运算符的运算顺序的, 比如:

1 + 2 +" blind mice";   // => "3 blind mice" 
1 + (2 +" blind mice"); // => "12 blind mice"

第一行没有圆括号,“+” 运算符具有从左至右的结合性, 因此两个数字首先进行加法计算,计算结果和字符串进行连接。 在第二行中, 圆括号改变了运算顺序:数字2和字符串连接,生成一个新字符串,然后数字1和这个新字符串再次连接,生成了最终结果。

一元算术运算符:

一元运算符作用于一个单独的操作数, 并产生一个新值。 在JavaScript中, 一元运算符具有很高的优先级, 而且都是右结合 (right-associative) 。一元算术运算符(+、 - 、++和-- ), 必要时, 它们会将操作数转换为数字。 需要注意的是, “+ ” 和 "-" 是一元运算符, 也是二元运算符。

一元加法(+):

一元加法运算符把操作数转换为数字(或者NaN) , 并返回这个转换后的数字。 如果操作数本身就是数字, 则直接返回这个数字。

一元减法(-):

当"-"用做一元运算符时, 它会根据需要把操作数转换为数字, 然后改变运算结果的符号。

递增(++)

递增"++" 运算符对其操作数进行增量(加一)操作, 操作数是一个左值 (value) (变量、 数组元素或对象属性)。 运算符将操作数转换为数字, 然后给数字加1, 并将加1后的数值重新赋值给变量、 数组元素或者对象属性。

递增"++"运算符的返回值依赖于它相对于操作数的位置。 当运算符在操作数之前, 称为 “前增量"(pre-increment)运算符, 它对操作数进行增量计算, 并返回计算后的值。 当运算符在操作数之后, 称为 “后增量"(post-increment)运算符, 它对操作数进行增量计算, 但返回未做增量计算的(unincremented)值。 思考 一下如下两行代码之间的区别:

var i = 1, j = ++i; // i和j的值都是2 
var i = 1, j = i++; // i是2, j是1

需要注意的是, 表达式++x并不总和x=x+1完全一样, “++” 运算符从不进行字符串连接操作, 它总是会将操作数转换为数字并增1。 如果x是字符串"1" , ++x的结果就是数字2, 而x+1是字符串"11"。

同样需要注意的是, 由于JavaScript会自动进行分号补全, 因此不能在后增量运算符和操作数之间插入换行符。 如果插入了换行符,JavaScript将会把操作数当做一条单独的语句, 并在其之前补上一个分号。

不管是前增量还是后增量,这个运算符通常用在for循环中, 用于控制循环内的计数器。

递减(--):

递减"--"运算符的操作数也是一个左值。 它把操作数转换为数字, 然后减1并将计算后的值重新赋值给操作数。 和"++"运算符一样, 递减"--"运算符的返回值依赖于它相对操作数的位置,当递减运算符在操作数之前, 操作数减1并返回减1之后的值。 当递减运算符在操作数之后, 操作数减1并返回减1之前的值。 当递减运算符在操作符的右侧时, 运算符和操作数之间不能有换行符。

位运算符

位运算符可以对由数字表示的二进制数据进行更低层级的按位运算。 尽管它们并不是传统的数学运算, 但这里也将其归类为算术运算符, 因为它们作用于数值类型的操作数并返回数字。 这些运算符在JavaScript编程中并不常用,这里的4个运算符都是对操作数的每个位进行布尔运算,这里将操作数的每个位当做布尔值(1=true, 0=false) , 其他三个位运算符用来进行左移位和右移位。

位运算符要求它的操作数是整数,这些整数表示为32位整型而不是64位浮点型。必要时,位运算符首先将操作数转换为数字,并将数字强制表示为32位整型,这会忽略原格式中的小数部分和任何超过32位的二进制位。移位运算符要求右操作数在0-31之间。在将其操作数转换为无符号32位整数后,它们将舍弃第5位之后的二进制位,以便生成一个位数正确的数字。需要注意的是,位运算符会将NaN、 Infinity和-Infinity都转换为0。

按位与(&)

位运算符"&"对它的整型操作数逐位执行布尔与(AND)操作。只有两个操作数中相对应的位都是1, 结果中的这一位才是1。

例如, 0x1234 & 0x00FF = 0x0034。

按位或(|)

位运算符"|"对它的整型操作数逐位执行布尔或 (OR) 操作。如果其中一个操作数相应的位为1, 或者两个操作数相应位都是1, 那么结果中的这一位就为1。

如: 0x1234 | 0x00FF = 0x12FF。

按位异或(^)

位运算符"|"对它的整型操作数逐位执行布尔异或(XOR)操作。异或是指第一个操作数为true或第二个操作数为true, 但两者不能同时为true。如果两个操作数中只有 一个相应位为1(不能同时为1)' 那么结果中的这 位就是1。 例如,

0xFF00 ^ 0xF0F0 = 0x0FF0。

按位非(~)

运算符"~"是 元运算符,位于一个整型参数之前, 它将操作数的所有位取反。根据JavaScript中带符号的整数的表示方法,对一个值使用"~"运算符相当于改变它的符号并减1。

例如,~0x0F = 0xFFFFFFF0或-16。

左移(<<)

将第一个操作数的所有二进制位记性左移操作。移动的位数由第二个操作数指定,移动的位数是0-31之间的一个整数。 例如,在表达式<<1中,a的第一位变成了第二位,a的第二位变成了它的第三位,以此类推。新的第一位用0来补充,舍弃第32位。将一个值左移1位相当于它乘以2,左移两位相当于乘以4,以此类推。如:7<<2=28。

带符号右移(>>)

位运算符">>"将第一个操作数的所有位进行右移操作,移动的位数由第二个操作数指定,移动的位数是0-31之间的一个整数。右边溢出的位将忽略。填补在左边的位由原操作数的符号决定,以便保持结果的符号与原操作数一致。如果第一个操作数是正数,移位后用0填补最高位;如果第一个操作数是负的,移位后就用1填补高位。将一个值右移1位,相当千用它除以2(忽略余数),右移两位,相当于它除以4,以此类推,例如,7>>1=3,-7>>1= -4。

无符号右移(>>>)

运算符">>>"和运算符">>"一样,只是左边的高位总是填补0,与原来的操作数符号无关,例如,-1>>4=-1,但是-1>>>4=0x0FFFFFFF。

关系表达式

JavaScript的关系运算符。关系运算符用于测试两个值之间的关系(比如“相等"'"小于",或“是…的属性")'根据关系是否存在而返回true或false。关系表达式总是返回一个布尔值,通常在if、while或者for语句中使用关系表达式,用以控制程序的执行流程。

接下来我们在来看一下相等和不等运算符、比较运算符和JavaScript中其他两个关系运算符in和instanceof。

相等和不等运算符

"=="和"==="运算符用于比较两个值是否相等,当然它们对相等的定义不尽相同。 两个运算符允许任意类型的操作数,如果操作数相等则返回true,否则返回false。

"==="也称为严格相等运算符(strictequality) (有时也称做恒等运算符(identity operator)) , 它用来检测两个操作数是否严格相等。

"=="运算符称做相等运算符(equality operator) , 它用来检测两个操作数是否相等,这里”相等”的定义非常宽松,可以允许进行类型转换。

JavaScript支持"="、"=="和"==="运算符。你应当理解这些(赋值、相等、恒等)运算符之间的区别,并在编码过程中小心使用。尽管它们都可以称做"相等" 但为了减少概念混淆,应该把"="称做"赋值",把"=="称做"相等",把"==="称做“严格相等”。

"!="和"!=="运算符的检测规则是"=="和"==="运算符的求反。如果两个值通过"=="的比较结果为true,那么通过"!="的比较结果则为false。

如果两值通过"==="的比较结果为true,那么通过"!=="的比较结果则为false。

"! "运算符是布尔非运算符。我们只要记住"!="称做"不相等"、"!=="称做“不严格相等”就可以了。

JavaScript对象的比较是引用的比较,而不是值的比较。对象和其本身是相等的, 但和其他任何对象都不相等。 如果两个不同的对象具有相同数量的属性, 相同的属性名和值, 它们依然是不相等的。 相应位置的数组元素是相等的两个数组也是不相等的。

严格相等运算符 "===" 首先计算其操作数的值, 然后比较这两个值, 比较过程没有任何类型转换:

  • 如果两个值类型不相同, 则它们不相等。
  • 如果两个值都是null或者都是undefined, 则它们不相等。
  • 如果两个值都是布尔值true或都是布尔值false, 则它们相等。
  • 如果其中一个值是NaN, 或者两个值都是NaN, 则它们不相等。 NaN和其他任何值都是不相等的, 包括它本身!通过x!==x来判断x是否为NaN, 只有在x为NaN的时候, 这个表达式的值才为true。
  • 如果两个值为数字且数值相等, 则它们相等。 如果一个值为0, 另一个值为-0, 则它们同样相等。
  • 如果两个值为字符串, 且所含的对应位上的16位数完全相等, 则它们相等。 如果它们的长度或内容不同, 则它们不等。 两个字符串可能含义完全一样且所显示出的字符也一样,但具有不同编码的16位值。JavaScript并不对Unicode进行标准化的转换, 因此像这样的字符串通过 "===" 和"==" 运算符的比较结果也不相等。
  • 如果两个引用值指向同一个对象、 数组或函数, 则它们是相等的。 如果指向不同的对象, 则它们是不等的, 尽管两个对象具有完全一样的属性。

相等运算符 "==" 和恒等运算符相似, 但相等运算符的比较并不严格。 如果两个操作数不是同一类型, 那么相等运算符会尝试进行一些类型转换, 然后进行比较:

  • 如果两个操作数的类型相同, 则和上文所述的严格相等的比较规则一样。 如果严格相等, 那么比较结果为相等。 如果它们不严格相等, 则比较结果为不相等。
  • 如果两个操作数类型不同, "==" 相等操作符也可能会认为它们相等。 检测相等将会遵守如下规则和类型转换:
  • 如果一个值是null, 另一个是undefined, 则它们相等。
  • 如果一个值是数字, 另一个是字符串, 先将字符串转换为数字, 然后使用转换后的值进行比较。
  • 如果其中一个值是true, 则将其转换为1再进行比较。 如果其中一个值是false,则将其转换为0再进行比较。
  • 如果一个值是对象, 另一个值是数字或字符串,将对象转换为原始值, 然后再进行比较。对象通过toString()方法或者valueOf()方法转换为原始值。JavaScript语言核心的内置类首先尝试使用valueOf(), 再尝试使用toString(), 除了日期类, 日期类只使用toString()转换。 那些不是JavaScript语言核心中的对象则通过各自的实现中定义的方法转换为原始值。
  • 其他不同类型之间的比较均不相等。

这里有 个判断相等的小例子:

"1"==true 

这个表达式的结果是true, 这表明完全不同类型的值比较结果为相等。 布尔值true首先转换为数字1, 然后再执行比较。接下来, 字符串 "1" 也转换为了数字1,因为两个数字的值相等, 因此比较结果为true。

比较运算符

比较运算符用来检测两个操作数的大小关系(数值大小或者字母表的顺序):

小于(<)

如果第一个操作数小于第二个操作数, 则 "<" 运算符的计算结果为true, 否则为false。

大于(>)

如果第一个操作数大于第二个操作数, 则 ">" 运算符的计算结果为true, 否则为false。

小于等于(<=)

如果第一个操作数小于或者等于第二个操作数, 则"<=" 运算符的计算结果为 true; 否则为false。

如果第一个操作数小于或者等于第二个操作数, 则"<=" 运算符的计算结果为 true; 否则为false。

如果第一个操作数大于或者等于第二个操作数, 则">=" 运算符的计算结果为false, 否则为false。

比较操作符的操作数可能是任意类型。然而, 只有数字和字符串才能真正执行比较操作, 因此那些不是数字和字符串的操作数都将进行类型转换, 类型转换规则如下:

如果操作数为对象, 那么这个对象将依照转换规则转换为原始值:如果valueOf()返回一个原始值,那么直接使用这个原始值。否则,使用toString()的转换结果进行比较操作。

在对象转换为原始值之后,如果两个操作数都是字符串,那么将依照字母表的顺序对两个字符串进行比较,这里提到的 ”字母表顺序” 是指组成这个字符串的16位Unicode字符的索引顺序。

在对象转换为原始值之后,如果至少有一个操作数不是字符串,那么两个操作数都将转换为数字进行数值比较。0和-0是相等的。Infinity比其他任何数字都大(除了Infinity本身),-Infinity比其他任何数字都小(除了它自身)。如果其中一个操作数是(或转换后是)NaN, 那么比较操作符总是返回false。

需要注意的是,JavaScript字符串是一个由16位整数值组成的序列,字符串的比较也只是两个字符串中的字符的数值比较。由Unicode定义的字符编码顺序和任何特定语言或者本地语言字符集中的传统字符编码顺序不尽相同。注意,字符串比较是区分大小写的,所有的大写的ASCII字母都 "小于"小写的ASCII字母。如果不注意这条不起眼的规则的话会造成一些小麻烦。比如,使用"<"小于运算符比较"Zoo"和"aardvark", 结果为true。

String. localCompare()方法更加可靠,这个方法参照本地语言的字母表定义的字符次序。对于那些不区分字母大小写的比较来说,则需要首先将字符串转全部换为小写字母或者大写字母,通过String.to Lowercase()和String.toUpperCase()做大小写的转换。

对于数字和字符串操作符来说,加号运算符和比较运算符的行为都有所不同,前者更偏爱字符串,如果它的其中一个操作数是字符串的话,则进行字符串连接操作。而比较运算符则更偏爱数字,只有在两个操作数都是字符串的时候,才会进行字符串的比较:

1 + 2          // 加法.结果是3

"1" + "2"      // 字符串连接,结果是"12"

"1" + 2        // 字符串链接,2转换为"2"'结果是"12" 
11 < 3         // 数字的比较,结果为false
"11" < "3"     // 字符串比较, 结果为true

"11 " < 3      // 数字的比较, "11"转换为11, 结果为false

"one" < 3      // 数字的比较, "one"转换为NaN, 结果为false

最后,需要注意的是,"<="(小于等于)和">=" (大于等于)运算符在判断相等的时候,并不依赖于相等运算符和严格相等运算符的比较规则。相反,小于等于运算符只是简单的 “不大于",大于等于运算符也只是"不小于" 。只有一个例外,那就是当其 一个操作数是(或者转换后是)NaN的时候,所有4个比较运算符均返回false。

in运算符

in运算符希望它的左操作数是一个字符串或可以转换为字符串,希望它的右操作数是一个对象。如果右侧的对象拥有一个名为左操作数值的属性名,那么表达式返回true,例如:

var point= { x:1, y:1 }; // 定义一个对象
"x" in point             // => true: 对象有一个名为"x"的属性


"z" in point            // => false: 对象中不存在名为"z"的属性
"toString" in point     // => true: 对象继承了toString()方法

var data= [7,8,9];      // 拥有三个元素的数组
"o" in data             // => true: 数组包含元素"o"
1 in data               // => true: 数字转换为字符串
3 in data               // => false: 没有索引为3的元素

instanceof运算符

instanceof运算符希望左操作数是一个对象,右操作数标识对象的类。如果左侧的对象是右侧类的实例,则表达式返回true,否则返回false。JavaScript中对象的类是通过初始化它们的构造函数来定义的。这样的话,instanceof的右操作数应当是一个函数。比如:

var d = new Date();   // 通过Date()构造函数创建一个新对象
d instanceof Date;    // 计算结果为true,d是由Date()创建的
d instanceof Object;  // 计算结果为true,所有对象都是object的实例
d instanceof Number;  // 计算结果为false,d不是一个Number对象
var a= [1, 2, 3];     // 通过数组直接量的写法创建一个数组
a instanceof Array;   // 计算结果为true,,a是一个数组
a instanceof Object;  // 计算结果为true,所有数组都是对象
a instanceof RegExp;  // 计算呢结果为false,数组不是正则表达式

需要注意的是,所有的对象都是Object的实例。当通过instanceof判断一个对象是否是一个类的实例的时候,这个判断也会包含对“父类"(superclass)的检测。如果instanceof的左操作数不是对象的话,instanceof返回false。如果右操作数不是函数,则抛出一个类型错误异常。

为了理解instanceof运算符是如何工作的,必须首先理解“原型链"(prototype chain)。原型链作为JavaScript的继承机制。

为了计算表达式o instanceof f, JavaScript首先计算f.prototype,然后在原型链中查找o,如果找到那么o是f(或者f的父类)的一个实例,表达式返回true。如果f.prototype不在o的原型链中的话那么o就不是f的实例,instanceof返回false。

逻辑表达式

逻辑运算符 "&&"、 "||" 和"!" 是对操作数进行布尔算术运算,经常和关系运算符一起配合使用, 逻辑运算符将多个关系表达式组合起来组成一个更复杂的表达式。

逻辑与(&&)

"&&"运算符可以从三个不同的层次进行理解。 最简单的第一层理解是, 当操作数都是布尔值的时候,"&&"对两个值执行布尔与(AND)操作, 只有在第一个操作数和第二个操作数都是true的时候, 它才返回true。如果其中一个操作数是false, 它返回false。

"&&"常用来连接两个关系表达式:

x == 0 && y == 0   // 只有在x和y都是0的时候, 才返回true

关系表达式的运算结果总是为true或false, 因此当这样使用的时候,"&&"运算符本身也返回true或false。 关系运算符的优先级比 "&&" (和 "||")要高, 因此类似这种表达式可以放心地书写, 而不用补充圆括号。

但是"&&"的操作数并不一定是布尔值, 有些值可以当做"真值"和"假值",假值是false、null、undefined、0、-0、NaN和"",所有其他的值包括所有对象都是真值。对“&&”的第二层理解是,"&&"可以对真值和假值进行布尔与(AND)操作。如果两个操作数都是真值,那么返回一个真值,否则至少一个操作数是假值的话,则返回一个假值。在JavaScript中任何希望使用布尔值的地方,表达式和语句都会将其当做真值或假值来对待。因此实际上"&&"并不总是返回true和false,但也并无大碍。

需要注意的是, 上文提到了运算符返回一个"真值"或者"假值",但并没有说明这个"真值"或者"假值"到底是什么值。 为此, 我们深入讨论对 "&&" 的第三层(也是最后一层)理解。 运算符首先计算左操作数的值, 即首先计算 "&&"左侧的表达式。 如果 计算结果是假值, 那么整个表达式的结果一定也是假值, 因此 "&&"这时简单地返回左操作数的值, 而并不会对右操作数进行计算。

反过来讲, 如果左操作数是真值, 那么整个表达式的结果则依赖于右操作数的值。 如果右操作数是真值, 那么整个表达式的值一定是真值,如果右操作数是假值, 那么整个表达式的值一定是假值。 因此, 当左操作数是真值时,"&&"运算符将计算右操作数的值并将其返回作为整个表达式的计算结果:

var o = {x : 1};
var p = null;
o && o.x // =>1:o是真值,因此返回值为o.x
p && p.x //=>null: p是假值,因此将其返回,而并不去计算p.x

这对于理解"&&"可能不会去计算右操作数的情况至关重要,在上述示例代码中,变量p的值是null, 而如果计算表达式p.x的话则会抛出一个类型错误异常。但是示例代码使用了"&&"的一种符合语言习惯的用法,因此只有在p为真值(不能是null或者 undefined)的情况下才会计算p.x。

"&&"的行为有时称做“短路"(short circuiting) , 我们也会经常看到很多代码利用了这一特性来有条件地执行代码。例如,下面两行JavaScript代码是完全等价的:

if (a == b) stop(); //只有在a==b的时候才调用stop()
(a == b) && stop(); //同上

一般来讲,当"&&"右侧的表达式具有副作用的时候(赋值、递增、递减和函数调用表达式)要格外小心。因为这些带有副作用的表达式的执行依赖于左操作数的计算结果。

尽管"&&"可以按照第二层和第三层的理解进行一些复杂表达式运算,但大多数情况

逻辑或(||)

"||"运算符对两个操作数做布尔或(OR)运算。如果其中一个或者两个操作数是真值,它返回一个真值。如果两个操作数都是假值,它返回一个假值。

尽管"||"运算符大多数情况下只是做简单布尔或(OR)运算,和"&&"一样,它也具有一些更复杂的行为。它会首先计算第个操作数的值,也就是说会首先计算左侧的表达式,如果计算结果为真值,那么返回这个真值。否则,再计算第二个操作数的值, 即计算右侧的表达式,并返回这个表达式的计算结果。

和"&&"运算符一样,同样应当避免右操作数包含一些具有副作用的表达式,除非你目地明确地在右侧使用带副作用的表达式,而有可能不会计算右侧的表达式。这个运算符最常用的方式是用来从一组备选表达式中选出第一个真值表达式:

//如果max_width已经定义了,直接使用它1否则在preferences对象中查找max_width
//如果没有定义它,则使用一个写死的常量
var max= max_width I I preferences.max_width I I soo; 这种惯用法通常用在函数体内,用来给参数提供默认值:
//将o的成员属性复制到p中, 并返回p 
function copy(o, p) { 
    p=pll{};11如果向参数p没有传入任何对象,则使用一个新创建的对象
    //函数体内的主逻辑
}

逻辑非(!)

"!" 运算符是一元运算符。 它放置在一个单独的操作数之前。 它的目的是将操作数的布尔值进行求反。 例如, 如果x是真值, 则!x返回false, 如果x是假值, 则!x返回true。

和"&&" 与 "||" 运算符不同, "!" 运算符首先将其操作数转换为布尔值, 然后再对布尔值求反。 也就是说"!" 总是返回true或者false, 并且, 可以通过使用两次逻辑非运算来得到一个值的等价布尔值: ! !x 。

作为一个一元运算符, "!"具有很高的优先级, 并且和操作数紧密绑定在一起。 如果你希望对类似p && q的表达式做求反操作, 则需要使用圆括号: ! (p && q)。 布尔计算的更多原理性知识不必要做过多的解释, 这里仅用JavaScript代码做简单说明:

// 对于p和q取任意值, 这两个等式都永远成立

!(p && q) === ! P || ! q 
!(p || q) === !p && !q

赋值表达式

JavaScript使用"=" 运算符来给变量或者属性赋值。 例如:

1 = 0 //将变址i设置为0
o.x = 1 //将对象o的属性x设置为1

"=" 运算符希望它的左操作数是一个左值: 一个变量或者对象属性(或数组元素)。 它的右操作数可以是任意类型的任意值。 赋值表达式的值就是右操作数的值。 赋值表达式的副作用是, 右操作数的值赋值给左侧的变量或对象属性, 这样的话, 后续对这个变最和对象属性的引用都将得到这个值。

尽管赋值表达式通常非常简单, 但有时仍会看到一些复杂表达式包含赋值表达式的情况。 例如, 可以将赋值和检测操作放在一个表达式中, 就像这样:

(a = b) == o

如果这样做的话, 应当清楚地知道"=" 和"==" 运算符之间的区别,需要注意的是"="具有非常低的优先级, 通常在一个较长的表达式中用到了一条赋值语句的值的时候, 需要补充圆括号以保证正确的运算顺序。

赋值操作符的结合性是从右至左, 也就是说, 如果一个表达式中出现了多个赋值运算符, 运算顺序是从右到左。 因此, 可以通过如下的方式来对多个变量赋值:

i=j=k=0;  //把三个变量初始化为0

带操作的赋值运算

除了常规的赋值运算"=" 之外, JavaScript还支持许多其他的赋值运算符, 这些运算符将赋值运算符和其他运算符连接起来, 提供一种更为快捷的运算方式。 例如, 运算符"+="执行的是加法运算和赋值操作, 下面的表达式:

total+= sales tax

和接下来的表达式是等价的:

total= total+ sales_tax

运算符"+=" 可以作用于数字或字符串, 如果其操作数是数字, 它将执行加法运算和赋值操作1 如果操作数是字符串, 它就执行字符串连接操作和赋值操作。

这类运算符还包括"-="、 "*="、 "&=" 等。

赋值运算符如下表所示:

运算符示例等价于
+=a+=ba=a+b
-=a-=ba=a-b
*=a*=ba=a*b
/=a/=ba=a/b
%=a%=ba=a%b
<<=a<<=ba=a<<b
>>=a>>=ba=a>>b
>>>=a>>>=ba=a>>>b
&=a&=ba=a&b
|=a|=ba=a|b
^=a^=ba=a^b