1.用===,而不用==
JavaScript有两组相等运算符:===和!==、==和!=。===和!==这一组运算符会按照期望的方式工作。如果两个运算数类型一致且拥有相同的值,那么===返回true,而!==返回false。==和!=只有在两个运算数类型一致时才会做出正确的判断,如果两个运算数是不同的类型,会试图强制转换运算数的类型。转换的规则复杂且难以记忆,具体规则如下:
/'/'==/'0/'//false
0==/'/'//true
0==/'0/'//true
false==/'false/'//false
false==/'0/'//true
false==undefined//false
false==null//false
null==undefined//true
上面表达式如果全部使用===运算符,则都会返回true。
==和!=运算符缺乏传递性,需要引起警惕。所谓传递性就是:如果a==b为true,b==c为true,则a==c也为true。因此,在JavaScript开发中,建议永远不要使用==和!=,而选用===和!==运算符。
下面分别介绍一下===和==运算符的算法。
(1)===运算符的算法
在使用===来判断两个值是否相等时,如判断x===y,会先比较两个值的类型是否相同,如果不相同,直接返回false。如果两个值的类型相同,则接着根据x的类型展开不同的判断逻辑:
❑如果x的类型是Undefined或Null,则返回true。
❑如果x的类型是Number,只要x或y中有一个值为NaN,就返回false;如果x和y的数字值相等,就返回true;如果x或y中有一个是+0,另外一个是-0,则返回true。
❑如果x的类型是String,当x和y的字符序列完全相同时返回true,否则返回false。
❑如果x的类型是Boolean,当x和y同为true或false时返回true,否则返回false。
❑当x和y引用相同的对象时返回true,否则返回false。
(2)==运算符的算法
在使用==来判断两个值是否相等时,如判断x==y,如果x和y的类型一样,判断逻辑与===一样;如果x和y的类型不一样,==不是简单地返回false,而是会进行一定的类型转换。
❑如果x和y中有一个是null,另外一个是undefined,返回true,如null==undefined。
❑如果x和y中有一个类型是String,另外一个类型是Number,会将String类型的值转换成Number来比较,如3==/"3/"。
❑如果x和y中有一个类型是Boolean,会将Boolean类型的值转换成Number来比较,如true==1、true==/"1/"。
❑如果x和y中有一个类型是String或Number,另外一个类型是Object,会将Object类型的值转换成基本类型来比较,如[3,4]==/"3,4/"。
2.谨慎使用++和--
递增(++)和递减(--)运算符使程序员可以用非常简洁的风格去编码,如在C语言中,它们使得通过一行代码实现字符串的复制成为可能,例如:
for(p=src,q=dest;!*p;p++,q++)*q=*p;
事实上,这两个运算符容易形成一种不谨慎的编程风格。大多数的缓冲区溢出错误所造成的安全漏洞都是由于这种编码导致的。
当使用++和--时,代码往往变得过于紧密、复杂和隐晦。因此,在JavaScript程序设计中不建议使用它们,从而使代码风格变得更为整洁。
++和--运算符只能够作用于变量、数组元素或对象属性。下面的用法是错误的。
4++;
正确的用法如下:
var n=4;
n++;
++和--运算符的位置不同所得运算结果也不同。例如,下面的递增运算符是先执行赋值运算,然后再执行递加运算。
var n=4;
n++;//4
而下面的递增运算符是先执行递加运算,再进行赋值运算。
var n=4;
++n;
3.小心逗号运算符
逗号在JavaScript语言中表示连续运算,并返回最后运算的结果。例如,在下面这个示例中,JavaScript先运算第一个数值直接量,再运算第二个数值直接量,然后运算第三个数值直接量,最后运算第四个数值直接量,并返回最后一个运算值4。
var a=(1,2,3,4);
alert(a);//4
再如:
a=1,b=2,c=3;
等价于:
a=1;
b=2;
c=3;
作为运算符,逗号一般用在特殊环境中,即在只允许出现一个句子的地方,把几个不同的表达式句子合并成一个长句。在JavaScript实际开发中,逗号运算符常与for循环语句联合使用。例如,在下面这个简单的for循环结构中,通过连续的运算符在参数表达式中运算多个表达式,以实现传递或运算多个变量或表达式。
for(var a=10,b=0;a>b;a++,b+=2){
document.write(/"a=/"+a+/"b=/"+b+/"<br>/");
}
逗号运算符比较“怪异”,稍不留心就会出错。例如,在下面这个简单的示例中,变量a的返回值为1,而不是连续运算后的返回值4。
a=1,2,3,4;
alert(a);//1
第一个数值1先赋值给变量a,然后a再参与连续运算,整个句子的返回值为4,而变量a的返回值为1,代码演示如下:
alert((a=1,2,3,4));//4
alert(a=(1,2,3,4));//4
要确保变量a的值为连续运算,可以使用小括号逻辑分隔符强迫4个数值先进行连续运算,然后再赋值。
a=(1,2,3,4);
alert(a);//4
当使用var关键字来定义变量时,会发现a最终没有返回值。
var a=1,2,3,4;
alert(a);//null
要确保var声明的变量正确返回连续运算的值,需要使用小括号先强迫数值进行计算,最后再把连续运算的值赋值给变量a。
var a=(1,2,3,4);
alert(a);//4
4.警惕运算符的副作用
JavaScript运算符一般不会对运算数本身产生影响,如算术运算符、比较运算符、条件运算符、取逆、“位与”等。例如,a=b+c,其中的运算数b和c不会因为加法运算而导致自身的值发生变化。
但在JavaScript中还有一些运算符能够改变运算数自身的值,如赋值、递增、递减运算等。由于这类运算符自身的值会发生变化,在使用时会具有一定的副作用,特别是在复杂表达式中,这种副作用更加明显,因此在使用时应该时刻保持警惕。例如,在下面代码中,变量a经过赋值运算和递加运算后,其值发生了两次变化。
var a;
a=0;
a++;
alert(a);//1
再如:
var a;
a=1;
a=(a++)+(++a)-(a++)-(++a);
alert(a);//-4
如果直观地去判断,会错误地认为返回值为0,实际上变量a在参与运算的过程中,变量a的值是不断发生变化的。这种变化很容易被误解。为了方便理解,进一步拆解(a++)+(++a)-(a++)-(++a)表达式:
var a;
a=1;
b=a++;
c=++a;
d=a++;
e=++a;
alert(b+c-d-e);//-4
如果表达式中还包含其他能够引起自身值发生变化的运算,那么整个表达式的逻辑就无法用人的直观思维来描述了。因此,从代码可读性角度来考量:在一个表达式中不能够对相同操作数执行两次或多次引起自身值变化的运算,除非表达式必须这样执行,否则应该避免产生歧义。这种歧义在不同编译器中会产生完全不同的解析结果。例如,下面的代码虽然看起来让人头疼,但由于每个运算数仅执行了一次引起自身值变化的运算,所以不会产生歧义。
a=(b++)+(++c)-(d++)-(++e);