文章目录
- JavaScript运算符完全攻略
JavaScript运算符完全攻略
JavaScript 定义了 47个运算符,另有 4 个存在争议的运算符。它们具有多重功能,在不同环境中可能会执行不同的操作,而且它们拥有更高的优先级(15级)。简单说明如下:
- .(点号):读、写对象的属性,语法格式为“对象…属性”。
- [](中括号):读、写数组的元素,或者读、写对象的属性,语法格式为“数组[整数]”“对象[‘属性名称’]”。
- ()(小括号):定义函数、调用函数、表达式分组等,常用语法格式为“函数(参数)”“(表达式)”。
- new:创建实例对象或者调用函数,语法格式为“new类型”“new函数”。
操作数的个数
一般情况下,运算符与操作数配合才能使用。其中,运算符指定执行运算的方式,操作数提供运算的内容。例如,1 加 1 等于 2,用表达式表示就是“n=1+1”。其中,1 是被操作的数,符号+表示两个值相加的运算,符号=表示赋值运算,n 表示接受赋值的变量。
不同的运算符需要配合的操作数的个数不同,可以分为以下 3 类:
- 一元运算符:一个操作符仅对一个操作数执行某种运算,如取反、递加、递减、转换数字、类型检测、删除属性等运算。
- 二元运算符:一个运算符必须包含两个操作数。例如,两个数相加、两个值比较大。大部分运算符都需要操作数配合才能够完成运算。
- 三元运算符:一个运算符必须包含三个操作数。JavaScript 中仅有一个三元运算符——条件运算符?:(if语句的简化形式)。
操作数的类型
运算符操作的数据并不是随意的,大部分都有类型限制。例如加、减、乘、除四则运算要求参与的操作数必须是数值,逻辑运算要求参与的操作数必须是布尔值。另外,每个运算符执行运算之后,都会有明确的返回类型。
JavaScript 能够根据运算环境自动转换操作数的类型,以便完成运算任务。
在下面代码中,两个操作数都是字符串,于是 JavaScript 自动把它们转换为数字,并执行减法运算,返回数字结果。
console.log("10"-"20"); //返回-10
在下面代码中,数字 0 本是数值类型,JavaScript 会把它转换为布尔值 false,然后再执行条件运算。
console.log(0 ? 1 : 2); //返回2
在下面代码中,字符串 5 被转换为数字,然后参与大小比较运算,并返回布尔值。
console.log(3 > "5"); //返回false
在下面代码中,数字 5 被转换为字符编码,参与字符串的顺序比较运算。
console.log("a" > 5); //返回false
在下面代码中,加号运算符能够根据数据类型执行相加或者相连运算。
console.log(10 + 20); //返回30
console.log("10" + "20"); //返回"1020"
在下面代码中,布尔值 true 被转换为数字 1,参与乘法运算,并返回 5。
console.log(true * "5"); //返回5
运算符的优先级
运算符的优先级决定执行运算的顺序。例如,1+2*3 结果是 7。而不是 9,因为乘法优先级高,虽然加号位于左侧。
使用小括号可以改变运算符的优先顺序。例如,(1+2)*3 结果是 9,而不是7。
在下面代码中,第二行与第三行返回结果相同,但是它们的运算顺序是不同的。第二行先计算 5 减 2,最后赋值给变量 n,并显示变量 n 的值;而第三行先计算 5 减 2,再把结果赋值给变量 n,最后变量 n 乘以 2 ,并显示两者所乘结果。
console.log(n=5-2*2); //返回1
console.log(n=(5-2)*2); //返回6
console.log((n=5-2)*2); //返回6
注意:不正确的使用小括号也会引发异常。
console.log((1+n=5-2)*2); //返回异常
在上面代码中,加号运算符优先级高,先执行加运算,但是此时的变量 n 还是一个未知数,所以就会抛出异常。
运算符的结合性
一元运算符、三元运算符和赋值运算符都是按照先右后左的顺序进行结合并运算。
在下面代码中,右侧的 typeof 运算符先与数字 5 结合,运算结果是字符串“number”,然后左侧的 typeof 运算符再与返回的字符串“number”结合,运算结果是字符串“string”。
console.log(typeof typeof 5); //返回“string”
其运算数序使用小括号表示如下:
console.log(typeof (typeof 5)); //返回“string”
对于下面表达式,左侧加号先结合,1+2 等于 3;然后 3 与右侧加号结合,3+3 等于 6;6 再与右侧加号结合,6+4 等于 10;最后返回结果。
1+2+3+4
其运算顺序使用小括号表示如下:
((1+2)+3)+4
左值、赋值及其副作用
左值就是只能出现在赋值运算符左侧的值,在 JavaScript 中主要指变量、对象的属性、数组的元素。
运算符一般不会对操作数本身产生影响。例如,a=b+c,其中的操作数 b 和 c 不会因为加法运算而导致自身的值发生变化。不过,具有赋值功能的运算符能够改变操作数的值,进而潜在干扰程序的运行状态,并可能对后面的运算造成影响,因此具有一定的副作用,使用时应该保持警惕。具体说明如下:
- 赋值运算符=
- 附加操作的赋值运算符如+=、%=等
- 递增++或递减–运算符
- delete运算符(功能等同于赋值 undefined)
示例1
在下面代码中,变量 a 经过赋值运算和递加运算后,其值发生了两次变化。
var a = 0;
a++;
console.log(a); //返回1
示例2
在下面代码中,变量 a 在参与运算的过程中,其值不断的被改写,显然这个程序干扰了程序的正常运行结果。
var a = 1;
a = (a++) + (++a) - (a++) - (++a); //返回-4
拆解 (a++) + (++a) - (a++) - (++a) 表达式如下:
var a = 1;
b = a++;
c = ++a;
d = a++;
e = ++a;
console.log(b+c-d-e);
从可读性考虑,在一个表达式中最好不要对同一个操作数执行两次或多次赋值运算。
示例3
下面代码由于每个操作数仅执行了一次赋值运算,所以不会引发歧义,也不会干扰后续运算。
a = (b++) + (++c) - (d++) - (++e);
console.log(a); //返回-4
JS算术运算(加减乘除+求余数+取反+自增自减)
算术运算符包括:加+、减-、乘*、除/、求余运算符%、数值取反运算符-。
加法运算
示例1
注意特殊操作数的求和运算。
var n = 5; //定义并初始化任意一个数值
console.log(NaN + n); //NaN与任意操作数相加,结果都是NaN
console.log(Infinity + n); //Infinity与任意操作数相加,结果都是Infinity
console.log(Infinity + Infinity); //Infinity与Infinity相加,结果是Infinity
console.log((-Infinity) + (-Infinity)); //负Infinity相加,结果是负Infinity
console.log((-Infinity) + Infinity); //正负Infinity相加,结果是NaN
示例2
加运算符能够根据操作数的数据类型,决定是相加操作,还是相连操作。
console.log(1 + 1); //如果操作数都是数值,则进行相加运算
console.log(1 + "1"); //如果操作数中有一个是字符串,则进行相连运算
console.log(3.0 + 4.3 + ""); //先求和,再连接,返回"7.3"
console.log(3.0 + "" + 4.3); //先连接,再连接,返回"34.3"
//3.0转换为字符串3
在使用加法运算符时,应先检查操作数的数据类型是否符合需要。
减法运算
示例1
注意特殊操作数的减法运算。
var n = 5; //定义并初始化任意一个数值
console.log(NaN - n); //NaN与任意操作数相减,结果都是NaN
console.log(Infinity - n); //Infinity与任意操作数相减,结果都是Infinity
console.log(Infinity - Infinity); //Infinity与Infinity相减,结果是NaN
console.log((-Infinity) - (-Infinity)); //负Infinity相减,结果是NaN
console.log((-Infinity) - Infinity); //正负Infinity相减,结果是-Infinity
示例2
在减法运算中,如果操作数为字符串,先尝试把它转换为数值,再进行运算。如果有一个操作数不是数字,则返回 NaN。
console.log(2 - "1"); //返回1
console.log(2 - "a"); //返回NaN
使用值减去 0,可以快速把值转换为数字。例如 HTTP 请求中查询字符串一般都是字符串型数字,可以先把这些参数值减去 0 转换为数值。这与调用 parseFloat() 方法的结果相同,但减法更高效、快捷。减法运算符的隐性转换如果失败,则返回 NaN,这与使用 parseFloat() 方法执行转换时的返回值是不同的。
例如,对于字符串“100aaa”而言,parseFloat() 方法能够解析出前面几个数字,而对于减法运算符来说,则必须是完整的数字,才可以进行转换。
console.log(parseFloat("100aaa")); //返回100
console.log("100aaa" - 0); //返回NaN
对于布尔值来说,parseFloat() 方法能够把 true 转换为 1,把 false 转换为 0,而减法运算符视其为 NaN。
对于对象来说,parseFloat() 方法会尝试调用对象的 toString() 方法进行转换,而减法运算符先尝试调用对象的 valueOf() 方法进行转换,失败之后再调用 toString() 进行转换。
乘法运算
注意特殊操作数的乘法运算。
var n = 5; //定义并初始化任意一个数值
console.log(NaN * n); //NaN与任意操作数相乘,结果都是NaN
console.log(Infinity * n); //Infinity与任意非零正数相乘,结果都是Infinity
console.log(Infinity * (- n)); //Infinity与任意非零负数相乘,结果是-Infinity
console.log(Infinity * 0); //Infinity与0相乘,结果是NaN
console.log(Infinity * Infinity); //Infinity与Infinity相乘,结果是Infinity
除法运算
注意特殊操作数的除法运算。
var n = 5; //定义并初始化任意一个数值
console.log(NaN / n); //如果一个操作数是NaN,结果都是NaN
console.log(Infinity / n); //Infinity被任意数字除,结果是Infinity或-Infinity
//符号由第二个操作数的符号决定
console.log(Infinity / Infinity); //返回NaN
console.log(n / 0); //0除一个非无穷大的数字,结果是Infinity或-Infinity,符号由第二个操作数的符号决定
console.log(n / -0); //返回-Infinity,解释同上
求余运算
求余运算也称模运算例如:
console.log(3 % 2); //返回余数1
模运算主要针对整数进行操作,也适用于浮点数。例如:
console.log(3.1 % 2.3); //返回余数0.8000000000000003
示例
注意特殊操作数的求余运算。
var n = 5; //定义并初始化任意一个数值
console.log(Infinity % n); //返回NaN
console.log(Infinity % Infinity); //返回NaN
console.log(n % Infinity); //返回5
console.log(0 % n); //返回0
console.log(0 % Infinity); //返回0
console.log(n % 0); //返回NaN
console.log(Infinity % 0); //返回NaN
取反运算
取反运算符是一元运算符,也称一元减法运算符。
示例
注意特殊操作数的取反运算。
console.log(- 5); //返回-5。正常数值取负数
console.log(- "5"); //返回-5。先转换字符串数字为数值类型
console.log(- "a"); //返回NaN。无法完全匹配运算,返回NaN
console.log(- Infinity); //返回-Infinity
console.log(- (- Infinity)); //返回Infinity
console.log(- NaN); //返回NaN
与一元减法运算符相对应的是一元加法运算符,利用它可以快速把一个值转换为数值。
递增和递减
递增++和递减–运算就是通过不断的加 1 或减 1,然后把结果赋值给左侧操作数,以实现改变自身结果的一种简洁方法。
作为一元运算符,递增运算符和递减运算符职能作用于变量、数组元素或对象属性,不能作用于直接量。根据位置不同,可以分为 4 种运算方式:
- 前置递增(++n):先递增,再赋值。
- 前置递减(–n):先递减,再赋值。
- 后置递增(n++):先赋值,再递增。
- 后置递减(n–):先赋值,再递减。
示例
下面比较递增和递减的 4 种运算方式所产生的结果。
var a = b = c = 4;
console.log(a++); //返回4,先赋值,再递增运算结果不变
console.log(++b); //返回5,先递增,再赋值,运算结果加1
console.log(c++); //返回4,先赋值,再递增,运算结果不变
console.log(c); //返回5,变量的值加1
console.log(++c); //返回6,先递增,再赋值,运算结果加1
console.log(c); //返回6
递增和递减是相反的操作,在运算之前都会试图转换值为数值类型,如果失败则返回 NaN。
JS &&(与运算)详解
逻辑运算又称布尔代数,就是布尔值(true 和 false)的“算数”运算。逻辑运算符包括:逻辑与&&、逻辑或||和逻辑非!。
逻辑与运算
逻辑与运算(&&)是 AND 布尔操作。只有两个操作数都为 true 时,才返回 true,否则返回 false。具体描述如表所示。
第一个操作数 | 第二个操作数 | 运算结果 |
---|---|---|
true | true | true |
true | false | false |
false | true | false |
false | false | false |
逻辑与是一种短路逻辑,如果左侧表达式为 false,则直接短路返回结果,不再运算右侧表达式。运算逻辑如下:
第 1 步:计算第一个操作数(左侧表达式)的值。
第 2 步:检测第一个操作数的值。如果左侧表达式的值可转换为 false(如 null、undefined、NaN、0、""、false),那么就会结束运算,直接返回第一个操作数的值。
第 3 步:如果第一个操作数可以转换为 true,则计算第二个操作数(右侧表达式)的值。
第 4 步:返回第二个操作数的值。
示例1
下面代码利用逻辑与运算检测变量并进行初始化。
var user; //定义变量
(! user && console.log("没有赋值")); //返回提示信息“没有赋值”
等效于:
var user; //定义变量
if (! user){ //条件判断
console.log("变量没有赋值");
}
如果变量 user 的值为 0 或空字符串等假值转换为布尔值时,则为 false,那么当变量赋值之后,依然提示变量没有赋值。因此,在设计时必须确保逻辑与左侧的表达式返回值是一个可以预测的值。
var user = 0; //定义并初始化变量
(! user && console.log("变量没有赋值")); //返回提示信息“变量没有赋值”
右侧表达式不应该包含赋值、递增、递减和函数调用等有效运算,因为当左侧表达式为 false 时,则直接跳过右侧表达式,会给后面的运算带来潜在影响。
示例2
使用逻辑与运算符可以代替设计多重分支结构。
var n = 3;
(n == 1) && console.log(1);
(n == 2) && console.log(2);
(n == 3) && console.log(3);
( ! n ) && console.log("null");
上面代码等效于下面多重分支结构。
var n = 3;
switch(n){
case1:
console.log(1);
break;
case2:
console.log(2);
break;
case3:
console.log(3);
break;
default:
console.log("null");
逻辑与运算的操作数可以是任意类型的值,并返回原始表达式的值,而不是把操作数转换为布尔值再返回。
- 对象被转换为布尔值时为 true。例如,一个空对象与一个布尔值进行逻辑与运算。
console.log(typeof ({} && true)); //返回第二个操作数的值 true的类型:布尔型
console.log(typeof (true && {})); //返回第二个操作数的值 {}的类型:对象
- 如果操作数中包含 null,则返回值总是 null。例如,字符串 “null” 与 null 类型值进行逻辑与运算,不管位置如何,始终都返回 null。
console.log(typeof ("null" && null)); //返回null的类型:对象
console.log(typeof (null && "null")); //返回null的类型:对象
- 如果操作数中包含 NaN,则返回值总是 NaN。例如,字符串 “NaN” 与 NaN 类型值进行逻辑与运算,不管位置如何,始终都返回 NaN。
console.log(typeof ("NaN" && NaN)); //返回NaN的类型:数值
console.log(typeof (NaN && "NaN")); //返回NaN的类型:数值
- 对于 Infinity 来说,将被转换为 true,与普通数值一样参与逻辑与运算。
console.log(typeof ("Infinity" && Infinity)); //返回第二个操作数Infinity的类型:数值
console.log(typeof (Infinity && "Infinity")); //返回第二个操作数"Infinity"的类型:字符串
- 如果操作数中包含 undefined,则返回 undefined。例如,字符串 “undefined” 与 undefined 类型值进行逻辑与运算,不管位置如何,始终都返回 undefined。
console.log(typeof ("undefined" && undefined)); //返回undefined
console.log(typeof (undefined && "undefined")); //返回undefined
JS ||(或运算)详解
逻辑或运算||是布尔 OR 操作。如果两个操作数都为 true,或者其中一个为 true,就返回 true,否则就返回 false。具体如图所示。
逻辑或运算
第一个操作数 | 第二个操作数 | 运算结果 |
---|---|---|
true | true | true |
true | false | true |
false | true | true |
false | false | false |
逻辑或也是一种短路逻辑,如果左侧表达式为 true,则直接短路返回结果,不再运算右侧表达式。运算逻辑如下:
第 1 步:计算第一个操作数(左侧表达式)的值。
第 2 步:检测第一个操作数的值。如果左侧表达式的值可转换为 true,那么就会结束运算,直接返回第一个操作数的值。
第 3 步:如果第一个操作数可以转换为 false,则计算第二个操作数(右侧表达式)的值。
第 4 步:返回第二个操作数的值。
示例1
针对下面 4 个表达式:
var n = 3;
(n == 1) && console.log(1);
(n == 2) && console.log(2);
(n == 3) && console.log(3);
( ! n ) && console.log("null");
可以使用逻辑或对其进行合并:
var n = 3;
(n == 1) && console.log(1) ||
(n == 2) && console.log(2) ||
(n == 3) && console.log(3) ||
( ! n ) && console.log("null");
由于&&运算符的优先级高于||运算符的优先级,所以不必使用小括号进行分组。不过使用小括号分组后,代码更容易阅读。
var n = 3;
((n == 1) && console.log(1)) ||
((n == 2) && console.log(2)) ||
((n == 3) && console.log(3)) ||
(( ! n ) && console.log("null")) ||
逻辑与和逻辑或运算符具有以下 2 个特点:
- 在逻辑运算过程中,临时把操作数转换为布尔值,然后根据布尔值决定下一步的操作,但是不会影响操作数的类型和最后返回结果。
- 受控于第一个操作数,可能不会执行第二个操作数。
示例2
在下面条件分支中,由于 a=“string” 操作数可以转换为 true,则逻辑或运算就不再执行右侧的定义对象表达式。最后 console.log(b.a); 语句抛出异常。
if(a = "string" || (b = {
a : "string"
})
) console.log(b.a); //调用b的属性a
如果使用逻辑与运算,就可以避免上述问题。
if(a = "string" && (b = (
a : "string"
})
) console.log(b.a); //调用b的属性a,返回字符串“string”
示例3
下面代码设计了一个复杂的嵌套结构,根据变量 a 决定是否执行下一个循环。
var a = b = 2; //定义并连续初始化
if(a){ //条件结果
while(b ++ < 10){ //循环结构
console.log(b++); //循环执行语句
}
)
使用逻辑与和逻辑或运算符进行简化:
var a = b = 2; //定义并连续初始化
while(a && b ++ < 10) console.log(b++); //逻辑与运算符合并的多条件表达式
如果转换为如下嵌套结构就不能够继续使用上述表达式进行简化,因为下面的代码时先执行循环体,后执行条件检测。
while(b ++ < 10){ //先执行循环
if(a){ //再判断条件
console.log(b++);
}
}
JS !(非运算)详解
逻辑非运算!是布尔取反操作(NOT)。作为一元运算符,直接放在操作数之前,把操作数的值转换为布尔值,然后取反并返回。
示例1
下面列举一些特殊操作数的逻辑非运算返回值。
console.log( ! {} ); //如果操作数是对象,则返回false
console.log( ! 0 ); //如果操作数是0,则返回true
console.log( ! (n = 5)); //如果操作数是非零的任何数字,则返回false
console.log( ! null ); //如果操作数是null,则返回true
console.log( ! NaN ); //如果操作数是NaN,则返回true
console.log( ! Infinity ); //如果操作数是Infinity,则返回false
console.log( ! ( - Infinity )); //如果操作数是-Infinity,则返回false
console.log( ! undefined ); //如果操作数是undefined,则返回true
示例2
如果对操作数执行两次逻辑非运算操作,就相当于把操作数转换为布尔值。
console.log( ! 0 ); //返回true
console.log( ! ! 0 ); //返回false
JS大小比较(>、<、>=、<=)运算符
关系运算也称比较运算,需要两个操作数,运算返回值总是布尔值。
比较大小的运算符有 4 个,说明如表所示。
大小关系运算符
大小运算符 | 说明 |
---|---|
< | 如果第一个操作数小于第二个操作数,则返回true;否则返回 false |
<= | 如果第一个操作数小于或等于第二个操作数,则返回true;否则返回 false |
>= | 如果第一个操作数大于或等于第二个操作数,则返回true;否则返回 false |
> | 如果第一个操作数大于第二个操作数,则返回true;否则返回 false |
比较运算中的操作数可以是任意类型的值,但是在执行运算时,会被转换为数字或字符串,然后再进行比较。如果是数字,则比较大小;如果是字符串,则根据字符编码表中的编号值从左到右逐个比较每个字符。
具体说明如下:
- 如果两个操作数都是数字,或者一个是数值,另一个可以转换成数字,则将根据数字大小进行比较。
console.log( 4 > 3 );
console.log("4" > Infinity );
- 如果两个操作数都是字符串,则执行字符串比较。
console.log("4" >"3");
console.log("a" > "b");
console.log("ab" >"cb");
console.log("abd" > "abc");
-
如果一个操作数是数字,或者被转换为数字,另一个是字符串,或者被转换为字符串,则使用 parseInt() 将字符串转换为数字(对于非数字字符串,将被转换为 NaN),最后以数字方式进行比较。
-
如果一个操作数为 NaN,或者被转换为 NaN,则始终返回 false。
console.log("a" >"3"); //返回true,字符a编码为61,字符3编码为33
console.log("a" > 3); //返回false,字符a被强制转换为NaN
-
如果一个操作数是对象,则先使用 valueOf() 取其值,再进行比较;如果没有 valueOf() 方法,则使用 toString() 取其字符串表示,再进行比较。
-
如果一个操作数是布尔值,则先转换为数值,再进行比较。
-
如果操作数都无法转换为数字或字符串,则比较结果为 false。
字符比较是区分大小写的,一般小写字符大于大写字符。如果不区分大小写,则建议使用 toLowerCase() 或 toUpperCase() 方法把字符串统一为小写或大写形式之后再比较。
注意:为了设计可控的比较运算,建议先检测操作数的类型,主动转换类型。
JS判断相等或者不等于(== 、=== 、!=、!==)运算符
等值检测运算符包括 4 个,详细说明如表所示。
等值检测运算符
等值检测运算符 | 说明 |
---|---|
==(相等) | 比较两个操作数的值是否相等 |
!=(不想等) | 比较两个操作数的值是否不相等 |
===(全等) | 比较两个操作数的值是否相等,同时检测它们的类型是否相同 |
!==(不全等) | 比较两个操作数的值是否不相等,同时检测它们的类型是否不相同 |
在相等运算中,应注意以下几个问题:
- 如果操作数是布尔值,则先转换为数值,其中 false 转为 0,true 转换为 1。
- 如果一个操作数是字符串,另一个操作数是数字,则先尝试把字符串转换为数字。
- 如果一个操作数是字符串,另一个操作数是对象,则先尝试把对象转换为字符串。
- 如果一个操作数是数字,另一个操作数是对象,则先尝试把对象转换为数字。
- 如果两个操作数都是对象,则比较引用地址。如果引用地址相同,则相等;否则不等。
示例1
下面是特殊操作数的相等比较。
console.log("1" == 1); //返回true。字符串被转换为数字
console.log(true == 1); //返回true。true被转换为1
console.log(false == 0); //返回true。false被转换为0
console.log(null == 0); //返回false
console.log(undefined == 0); //返回false
console.log(undefined == null); //返回true
console.log(NaN == "NaN"); //返回false
console.log(NaN ==1); //返回false
console.log(NaN == NaN); //返回false
console.log(NaN != NaN); //返回true
NaN与任何值都不相等,包括它自己。null 和 undefined 值相等,但是它们是不同类型的数据。在相等比较中,null 和 undefined 不允许被转换为其他类型的值。
示例2
下面两个变量的值是相等的。
var a = "abc" + "d";
var b = "a" + "bcd";
console.log(a == b); //返回true
数值和布尔值的相等比较运算效率比较高,而字符串需要逐个字符进行比较,相等比较运算效率比较低。
在全等运算中,应注意以下几个问题:
- 如果两个操作数都是简单的值,则只要值相等,类型相同,就全等。
- 如果一个操作数是简单的值,另一个操作数是复合型对象,则不全等。
- 如果两个操作数都是复合型对象,则比较引用地址是否相同。
示例3
下面是特殊操作数的全等比较。
console.log(null === undefined); //返回false
console.log(0 === "0"); //返回false
console.log(0 === false); //返回false
示例4
下面是两个对象的比较,由于它们都引用了相同的地址,所以返回 true。
var a = {};
var b = a;
console.log(a === b); //返回true
下面两个对象虽然结构相同,但是地址不同,所以不全等。
var a = {};
var b = {};
console.log(a === b); //返回false
示例5
对于复合型对象,主要比较引用的地址,不比较对象的值。
var a = new String("abcd); //定义字符串“abcd”对象
var b = new String("abcd); //定义字符串“abcd”对象
console.log(a === b); //返回false
console.log(a == b); //返回false
在上面示例中,两个对象的值相等,但是引用地址不同,所以它们既不想等,也不全等。因此,对于复合型对象来说,相等和全等=运算的结果是相同的。
示例6
对于简单的值,只要类型相同、值相等,它们就是全等,不考虑表达式运算的过程变化,也不用考虑变量的引用地址。
var a = "1" + 1;
var b = "11";
console.log(a ===b); //返回true
示例7
表达式(a>b || a==b)与表达式(a>=b)并不完全相等。
var a = 1;
var b = 2;
console.log((a > b || a == b) == (a >= b)); //返回true,此时似乎相等
如果为变量 a 和 b 分别赋值 null 和 undefined,则返回值 false,说明这两个表达式并非完全等价。
var a = null;
var b = undefined;
console.log((a > b || a == b) == (a >= b)); //返回false,表达式的值并非相等
因为 null == undefined 等于 true,所以表达式(a > b || a == b)的返回值为 true,但是表达式 null >= undefined 的返回值为 false。
JS赋值运算符详解
赋值运算符左侧的操作数必须是变量、对象属性或数组元素,也称为左值。例如,下面的写法是错误的,因为左侧的值是一个固定的值,不允许操作。
1 = 100; //返回错误
赋值运算有以下两种形式:
- 简单的赋值运算=:把等号右侧操作数的值直接复制给左侧的操作数,因此左侧操作数的值会发生变化。
- 附加操作的赋值运算:赋值之前先对右侧操作数执行某种操作,然后把运算结果复制给左侧操作数。具体说明如表所示。
附加操作的赋值运算符
赋值运算符 | 说明 | 示例 | 等效于 |
---|---|---|---|
+= | 加法运算或连接操作并赋值 | a += b | a = a + b |
-= | 减法运算并赋值 | a -= b | a= a - b |
*= | 乘法运算并赋值 | a *= b | a = a * b |
/= | 除法运算并赋值 | a /= b | a = a / b |
%= | 取模运算并赋值 | a %= b | a = a % b |
<<= | 左移位运算并赋值 | a <<= b | a = a << b |
>>= | 右移位运算并赋值 | a >>= b | a = a >> b |
>>>= | 无符号右移位运算并赋值位 | a >>>= b | a = a >>> b |
&= | 位与运算并赋值 | a &= b | a = a & b |
= | 位或运算并赋值 | a = b | a = a = b |
^= | 位异或运算并赋值 | a ^= b | a = a ^ b |
示例1
使用赋值运算符设计复杂的连续赋值表达式。
var a = b = c = d = e = f = 100; //连续赋值
//在条件语句的小括号内进行连续赋值
for((a = b = 1;a < 5;a++) {console.log(a + "" + b)};)
赋值运算的结合性是从右向左,最右侧的赋值运算先执行,然后再向左赋值,以此类推,所以连续赋值运算不会引发异常。
示例2
在下面表达式中,逻辑与左侧的操作数是一个赋值表达式,右侧的操作数也是一个赋值表达式。但是左侧赋的值是一个简单值,右侧是把一个函数赋值给变量b。
var a;
console.log(a = 6 && (b = function(){
return a;
})()
);
在逻辑与运算中,左侧的赋值并没有真正的复制给变量 a,当逻辑与运算执行右侧的表达式时,该表达式是把一个函数赋值给变量 b,然后利用小括号运算符调用这个函数,返回变量 a 的值,结果并没有返回变量 a 的值 6,而是 undefined。
由于赋值运算作为表达式使用具有副作用,使用时要慎重,确保不会引发风险。对上面的表达式更安全的写法如下:
var a = 6; //定义并初始化变量a
b = function () { //定义函数对象b
return a;
}
console.log(a && b()); //逻辑与运算,根据a决定是否调用函数b
JS对象操作(in、instanceof、delete)运算符
对象操作运算主要是针对对象、数组、函数这 3 类复合型对象执行某种程序,设计的运算符包括 in、instanceof、delete。
归属检测
in运算符能够检测左侧操作数是否为右侧操作数的成员。其中,左侧操作数是一个字符串,或者可以转换为字符串的表达式,右侧操作数是一个对象或数组。
示例1
下面代码使用 in 运算符检测属性 a、b、c、valueOf 是否为对象 o 的成员。
var o = { //定义对象
a : 1, //定义属性a
b : function() {} //定义方法b
}
console.log("a" in o); //返回true
console.log("b" in o); //返回true
console.log("c" in o); //返回false
console.log("valueOf" in o); //返回true,继承Object的原型方法
console.log("constructor" in o); //返回true,继承Object的原型属性
instanceof运算符能够检测左侧的对象是否为右侧类型的示例。
示例2
下面代码使用 instanceof 检测数组 a 是否为 Array、Object 和 Function 的示例。
var a = new Array(); //定义数组
console.log(a instanceof Array); //返回true
console.log(a instanceof Object); //返回true,Array是Object的子类
console.log(a instanceof Function); //返回false
如果左侧操作数不是对象,或者操作数不是类型函数,则返回 false。如果右侧操作数不是复合型对象,则将返回错误。
删除属性
delete运算符能够删除指定对象的属性、数组元素或变量。如果删除操作成功,则返回 true;否则返回 false。
示例1
下面代码使用 delete 运算符删除对象 a 的属性 x。
var a = { //定义对象a
x : 1, //定义对象成员
y : 2 //定义对象成员
};
console.log(a.x); //调用对象成员,返回1
console.log(delete a.x); //删除对象成员x成功,返回true
console.log(a.x); //返回undefined,没有找到该对象成员
部分 JavaScript 内置成员和客户端成员,以及使用 var 语句声明的变量不允许删除。
c = 1; //初始化变量c,没有使用var语句声明
console.log(delete c); //返回true,说明删除成功
var b = 1; //使用var语句声明并初始化变量
console.log(delete b); //返回false,说明不允许删除
console.log(delete Object.constructor); //返回true,说明部分内部成员可以被删除
示例2
如果删除不存在的对象成员、数组元素、变量,则返回 true。因此,使用 delete 运算符时,要注意区分成功删除与无效操作。
var s = {}; //定义对象a
console.log(delete a); //返回false,说明不允许删除
console.log(delete a.z); //返回true,说明不存在该属性
console.log(delete b); //返回true,说明不存在该变量
示例3
下面代码使用 delete 运算符,配合 in 运算符,实现对数组成员执行检测、插入、删除或更新操作。
var a = []; //定义数组对象
if("x" in a){ //如果对象a中存在x
delete a["x"]; //则删除成员x
}else{ //如果不存在成员x
a["x"] = true; //则插入成员x,并为其赋值true
}
console.log(a.x); //返回true。查看成员x的值
if(delete a["x"]){ //如果删除成员x成功
a["x"] = false; //更新成员x的值为false
}
console.log(a.x); //返回false。查看成员x的值
JS &、|、^和~(逻辑位运算符)
位运算就是对二进制数执行计算,是整数的逐位运算。例如,1+1=2,在十进制计算中是正确的,但是在二进制计算中,1+1=10;对于二进制数 100 取反,等于 001,而不是 -100。
位运算符有 7 个,分为两类:
- 逻辑位运算符:位与(&)、位或(|)、位异或(^)、非位(~)
- 移位运算符:左移(<<)、右移(>>)、无符号右移(>>>)
逻辑位运算符与逻辑运算符的运算方式是相同的,但是针对的对象不同。逻辑位运算符针对的是二进制的整数值,而逻辑运算符针对的是非二进制的值。
“&”运算符
“&”运算符(位与)用于对两个二进制操作数逐位进行比较,并根据下表所示的换算表返回结果。
“&”运算符
第一个数的位值 | 第二个数的位值 | 运算结果 |
---|---|---|
1 | 1 | 1 |
1 | 0 | 0 |
0 | 1 | 0 |
0 | 0 | 0 |
在位运算中,数值 1 表示 true,0 表示 false,反之亦然。
12 和 5 进行位与运算,则返回值为 4。
console.log(12 & 5); //返回值4
下图以算式的形式解析了 12 和 5 进行位与运算的过程。通过位与运算,只有第 3 位的值为全为 true,故返回 true,其他位均返回 false。
“|”运算符
“|”运算符(位或)用于对两个二进制操作数逐位进行比较,并根据如表格所示的换算表返回结果。
“|”运算符
第一个数的位值 | 第二个数的位值 | 运算结果 |
---|---|---|
1 | 1 | 1 |
1 | 0 | 1 |
0 | 1 | 1 |
0 | 0 | 0 |
12 和 5 进行位或运算,则返回值为 13。
console.log(12 | 5); //返回值13
下图以算式的形式解析了 12 和 5 进行位或运算的过程。通过位或运算,除第 2 位的值为 false 外,其他位均返回 true。
“^”运算符
“^”运算符(位异或)用于对两个二进制操作数逐位进行比较,并根据如表格所示的换算表返回结果。
“^”运算符
第一个数的位值 | 第二个数的位值 | 运算结果 |
---|---|---|
1 | 1 | 0 |
1 | 0 | 1 |
0 | 1 | 1 |
0 | 0 | 0 |
12 和 5 进行位异或运算,则返回值为 9。
console.log(12 ^ 5); //返回值9
下图以算式的形式解析了 12 和 5 进行位异或运算的过程。通过位异或运算,第 1、4 位的值为 true,而第 2、3 位的值为 false。
“~”运算符
“~”运算符(位非)用于对一个二进制操作数逐位进行取反操作。
第 1 步:把运算数转换为 32 位的二进制整数。
第 2 步:逐位进行取反操作。
第 3 步:把二进制反码转换为十进制浮点数。
对 12 进行位非运算,则返回值为 -13。
console.log( ~ 12 ); //返回值-13
下图以算式的形式解析了对 12 进行位非运算的过程。
位非运算实际上就是对数字进行取负运算,再减 1。例如:
console.log( ~ 12 == 12-1); //返回true
JS移位运算符(<<、>>和>>>)
移位运算就是对二进制进行有规律低移位。移位运算可以设计很多奇妙的效果,在图形图像编程中应用广泛。
“<<”运算符
“<<”运算符执行左移位运算。在移位运算过程中,符号位始终保持不变。如果右侧空出位置,则自动填充为 0;超出 32 位的值,则自动丢弃。
把数字 5 向左移动 2 位,则返回值为 20。
console.log(5 << 2); //返回值20
用算式进行演示,如图所示。
“>>”运算符
“>>”运算符执行有符号右移位运算。与左移运算操作相反,它把 32 位数字中的所有有效位整体右移,再使用符号位的值填充空位。移动过程中超出的值将被丢弃。
把数值 1000 向右移 8 位,则返回值为 3。
console.log(1000 >> 8); //返回值3
用算式进行演示,如图所示。
把数值 -1000 向右移 8 位,则返回值为 -4。
console.log(-1000 >> 8); //返回值 -4
用算式进行演示,如图所示。当符号位值为 1 时,则有效位左侧的空位全部使用 1 进行填充。
“>>>”运算符
“>>>”运算符执行五符号右移位运算。它把无符号的 32 位整数所有数位整体右移。对于无符号数或正数右移运算,无符号右移与有符号右移运算的结果是相同的。
下面两行表达式的返回值是相同的。
console.log(1000 >> 8); //返回值3
console.log(1000 >> 8); //返回值3
对于负数来说,无符号右移将使用 0 来填充所有的空位,同时会把负数作为正数来处理,所得结果会非常大所以,使用无符号右移运算符时要特别小心,避免意外错误。
console.log(-1000 >> 8); //返回值 -4
console.log(-1000 >>> 8); //返回值 16777212
用算式进行演示,如图所示。左侧空位不再用符号位的值来填充,而是用 0 来填充。
JS条件运算符(?:)详解
条件运算符是唯一的三元运算符,其语法格式如下:
b ? x : y
b 操作数必须是一个布尔型的表达式,x 和 y 是任意类型的值。
如果操作数 b 的返回值为 true,则执行 x 操作数,并返回该表达式的值。
如果操作数 b 的返回值为 false,则执行 y 操作数,并返回该表达式的值。
定义变量 a,然后检测 a 是否被赋值,如果赋值则使用该值;否则设置默认值。
var a = null; //定义变量a
typeof a != "undefined" ? a = a : a = 0; //检测变量a是否赋值,否则设置默认值
console.log(a); //显示变量a的值,返回null
条件运算符可以转换为条件结构:
if(typeof a != "undefined"){ //赋值
a = a;
}else{ //没有赋值
a = 0;
}
console.log(a);
也可以转换为逻辑表达式:
(typeof a != "undefined") && (a =a) || (a = 0); //逻辑表达式
console.log(a);
在上面表达式中,如果 a 已赋值,则执行 (a = a) 表达式,执行完毕就不再执行逻辑或运算符后面的 (a = 0) 表达式;如果 a 未赋值,则不再执行逻辑与运算符后面的 (a = a) 表达式,转而执行逻辑或运算符后面的表达式 (a = 0)。
注意: 在实战中需要考虑假值的干扰。使用 typeof a != “undefined” 进行检测,可以避开变量赋值为 false、null、""、NaN等假值时,也被误认为没有赋值。
JS逗号运算符(,)详解
逗号运算符是二元运算符,它能够先执行运算符左侧的操作数,然后再执行右侧的操作数,最后返回右侧操作数的值。
逗号运算符可以实现连续运算,如多个变量连续赋值。
var a = 1,b = 2,c = 3,d = 4;
等价于:
var a = 1;
var b = 2;
var c = 3;
var d = 4;
与条件运算符、逻辑运算符根据条件来决定是否执行所有或特定操作数不同的是,逗号运算符会执行所有的操作数,但并非返回所有操作数的结果,它只返回最后一个操作数的值。
在下面代码中,变量 a 的值是逗号运算之后,通过第二个操作数 c=2 的执行结果赋值得到的。第一个操作数的执行结果没有返回,但是这个表达式被执行了。
a = (b = 1,c = 2); //连续执行和赋值
console.log(a); //返回2
console.log(b); //返回1
console.log(c); //返回2
提示
逗号运算符可以作为仅需执行表达式的工具,这些表达式不需要返回值,但必须要运算。在特定环境中,可以在一个表达式中包含多个子表达式,通过逗号运算符让它们全部执行,而不用返回结果。
for 循环结构的小括号内包含 3 个表达式,第一个表达式为初始化值,第二个表达式为检测条件,第三个表达式为递增表达式。使用逗号运算符可以在 3 个表达式中添加多个额外的计算任务,但要确保第二个表达式的最后一个子表达式返回一个可控布尔值,否则会导致死循环。
for(var a = 1,b = 10,c = 100;++ c,a < b;a ++,c --){
console.log(a * c);
}
逗号运算符的优先级是最低的。在下面代码中,赋值运算符优先于逗号运算符,也就是说数值 1 被赋值给变量 b 之后,继续赋值给变量 a,最后才执行逗号运算符。
a = b = 1,c = 2; //连续执行和赋值
console.log(a); //返回1
console.log(b); //返回1
console.log(c); //返回2
JS void运算符
void 是一元运算符,它可以出现在任意类型的操作数之前执行操作数,却忽略操作数的返回值,返回一个 undefined。void 常用语 HTML 脚本中执行 JavaScript 表达式,但不需要返回表达式的计算结果。
示例1
在下面代码中,使用 void 运算符让表达式返回 undefined。
var a = b = c = 2; //定义并初始化变量的值
d = void (a -= (b *= (c += 5))); //执行void运算符,并把返回值赋予变量d
console.log(a); //返回-12
console.log(b); //返回14
console.log(c); //返回7
console.log(d); //返回undefined
由于 void 运算符的优先级比较高(14),高于普通运算符的优先级,所以在使用时应该使用小括号明确 void 运算符操作的操作数,避免引发错误。
示例2
在下面两行代码中,由于第一行代码没有使用小括号运算符,则 void 运算符优先执行,返回值 undefined 再与 1 执行减法运算,所以返回值为 NaN。在第二行代码中由于使用小括号运算符明确 void 的操作数,减法运算符先被执行,然后执行 void 运算,最后返回值是 undefined。
console.log(void 2 - 1); //返回NaN
console.log(void (2 - 1)); //返回undefined
示例3
void 运算符也能像函数一样使用,如 void(0) 也是合法的。在特殊环境下一些复杂的语句可能不方便使用 void 运算符,而必须使用 void 函数。
console.log(void (i = 0)); //返回undefined
console.log(void (i = 0;i++)); //返回undefined
JS new运算符
new 是一个运算符,可以创建对象,初始化实例。其语法格式如下:
new contructor(arguments)
constructor 必须是一个构造函数表达式,参数 arguments 可有可无,参数之间用逗号分隔。如果没有逗号,可以省略小括号。
在最新的参考资料中,new 已从运算符列表中被移出,作为命令使用,扮演多重角色。
示例1
下面代码使用 new 运算符实例化 Array,并演示 3 种不同的使用方法。
var a = new Array; //创建数组,省略小括号
var b = new Array(); //创建数组
var c = new Array(1,2,3); //创建数组,并初始化元素值
console.log(c[2]); //读取创建数组对象的元素值,返回3
new 被执行时,首先会创建一个新对象,然后调用指定的构造函数,并根据传入参数初始化实例最后把初始化的实例传递给新对象。
示例2
下面代码自定义类,然后使用它创建新的对象。
var a = function(){ //自定义类型函数a
this.x = 1; //属性x
this.y = 2; //属性y
};
var b = new a; //创建对象
console.log(b.x); //调用对象属性x,返回1