JavaScript运算符详解

JavaScript运算符详解

在讲解运算符之前,先问个问题:var a = b && c || d,变量a是不是布尔值?为什么?

JavaScript中的运算符大致可以分为以下几类:

  1. 算术运算符
  2. 比较运算符
  3. 布尔值运算符
  4. 二进制位运算符
  5. 其它运算符

1. 算术运算符

javaScript提供了以下算术运算符:

  • 加法运算符
  • 减法运算符
  • 乘法运算符
  • 除法运算符
  • 指数运算符
  • 余数运算符
  • 自增运算符
  • 自减运算符
  • 正负数值运算符

减法,乘法,除法运算就是正常的数学计算,这里不做详细讲解。

1.1 加法运算符

1.1.1 基本运算规则

加法运算符(+),用来求两个运算子的和。但是JavaScript中,允许非数值相加,而且涉及到字符串时,加法运算符会变成连接运算符。

// 正常的数值相加
1 + 1; // 2

// 非数值相加,布尔值会自动转换为数值,然后进行加法运算
true + true ; // 2
false + false; // 0
true + false; // 1
1 + true; // 2
1 + false; // 1 

// 运算子有字符串时,变为连接运算符
"hello " + "world!"; // "hello world!"
1 + "a"; // "1a"
true + "a"; // "truea"

加法运算符是在运行时决定,到底是执行相加,还是执行连接。这种现象称为“重载”。

// 加法运算的重载
1 + 1 + "a"; // "2a"
"a" + 1 + 1; // "a11"

1.1.2 对象的相加

如果运算子是对象,必须先转成原始类型的值,然后再相加。

var x = { name: "jidi" };
x + 2 // "[object Object]2"

对象转换为原始类型的值,规则如下:

  1. 调用对象的valueOf方法(一般情况下,对象的valueOf方法总是返回对象自身)。
  2. 调用对象的toString方法(对象的toString方法默认返回[Object,Object])。

上面那个例子,实际上可以看成是:

var x = { name: "jidi" };

var y = x.valueOf(); // {name: "jidi"}

var z = y.toString(); // "[object Object]"

z + 2; // "[object Object]2"

我们可以自己定义valueOftoString方法。以得到想要的结果。

// 自定义valueOf方法
var x = {
	name: "jidi",
	valueOf: function(){
		return 5;
	}
};
// 自定义toString方法
var y = {
	name: "jidi",
	toString: function(){
		return 10;
	}
}

5 + x; // 10
5 + y; // 15

注意:如果参与加法运算的两个运算子,其中有一个是Date对象实例,会先执行toStrnig方法。

var now = new Date();

now.valueOf = function(){
	return 5;
}
now.toString = function(){
	return 10;
}

5 + now; // 15

1.2 余数运算符

余数运算符(%)返回前一个运算子被后一个运算子除,所得的余数。余数运算的结果的正负号由第一个运算子决定。

12 % 5; // 2

// 余数运算结果正负情况由第一个运算子正负情况决定
-12 % 5; // -2
-12 % -5; // -2

1.3 自增和自减运算符

自增和自减运算符,是一元运算符,只需要一个运算子。它们的作用是将运算子首先转为数值,然后加上1或者减去1。它们会修改原始变量。

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

--x // 1
x // 1

自增和自减是仅有的两个具有运算副作用(参与运算后,变量的值发生改变)的运算符。

自增和自减运算符又分为前置和后置两种:前置自增(自减),会先进行自增(自减),然后返回变量操作后的值;后置自增(自减)会先返回变量操作之前的值,然后再进行自增(自减)操作。

var x = 1;
var y = 1;

x++ // 1
++y // 2

1.4 正负数值运算符

数值运算符(+)是一元运算符,可以将任何数值转为数值。
负数值运算符(-)也是一元运算符,可以将任何数值转为数值,只是结果值正负相反。

var x = 1;

// 正数值运算符
+x; // 1
+true; // 1
+[]; // 0

// 负数值运算符
-x; // -1
-true; // -1

1.5 指数运算符

指数运算符(**)是二元运算符,前一个运算子是底数,后一个运算子是指数。指数运算是右结合

5 ** 2; // 25

// 指数运算的右结合
// 相当于5 ** (2 ** 2)
5 ** 2 ** 2; //  625

1.6 赋值运算符

赋值运算符用于给变量赋值。但是赋值运算符可以与其他运算符结合,形成变体。

// 普通赋值运算符

var x = 2; // 将数值2赋值给变量x
var y = x; // 将变量x赋值给变量y

// 与其他运算符结合使用
x += 2; // 等价于 x = x + 2;
x -= 2; // 等价于 x = x - 2;
x *= 2; // 等价于 x = x * 2;
x /= 2; // 等价于 x = x / 2;
x %= 2; // 等价于 x = x % 2;
x **= 2; // 等价于 x = x ** 2;

2. 比较运算符

比较运算符用来比较两个值的大小,返回一个布尔值。
JavaScript中提供了以下比较运算符:

  • >,大于运算符
  • <,小于运算符
  • >=,大于等于运算符
  • <=,小于等于运算符
  • ==,等于运算符
  • ===,严格相等运算符
  • !=,不相等运算符
  • !==,严格不相等运算符

上述运算符可以分为两类:相等比较运算符与非相等比较运算符,两者规则不一样。

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

对于非相等运算符,如果运算子都是字符串,则是按照字典顺序进行比较。JavaScript引擎会首先比较首字符的Unicode码点,如果相等,则比较第二个字符的Unicode码点,以此类推…

"this".charCodeAt(); // 首字符Unicode码点为116
"is".charCodeAt(); // 首字符Unicode码点为105
"this" > "is"; // 116>105,true

"This".charCodeAt(); // 首字符Unicode码点为84
"this">"This"; // true

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

如果两个运算子中至少有一个不是字符串,则存在以下情况:

(1) 原始类型值

如果两个运算子都为原始类型值,则先转换成数值,然后再进行比较。但是任何值与NaN比较,返回值都为false

4 > "3"; // true
2 > true; // true
1 < false; // false
1 > null; // true

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

注意:underfined转换为数值会转换成为NaN,即任何值与underfinedNaN进行比较,结果都是false

(2) 对象
如果运算子是对象,则会转换成为原始类型的值,然后再进行比较。
对象转换为原始类型的值按照以下步骤进行的:

  1. 调用对象的valueOf方法(对象调用valueOf方法一般返回对象自身),如果返回原始类型的值,不再进行后续步骤。
  2. 如果valueOf方法结果仍旧是对象,则调用toString方法(对象的toString方法默认返回[Object,Object])。
var x = [2];
x.valueOf(); // [2]
x.valueOf().toString(); // "2"
x > "11" // true ,等同于 [2].valueOf().toString() > "11",即"2">“11”

// 改写valueOf方法
x.valueOf = function () { return "1" };
x > "11" // false,等同于 [2].valueOf() > "11",即 "1" > "11"

上述例子中,数组x调用valueOf方法返回x本身,然后调用toString方法,返回"2",此时是"2""11"两个字符串之间进行比较,比较的是 Unicode码点位置。

2.3 严格相等运算符

严格相等运算符(===)是二元运算符,比较的是两个运算子是否为同一个值。参与运算的运算子情况不同,具有不同的结果:

  • 如果两个运算子值类型不同,直接返回false
  • 如果两个运算子值类型相同,且都是原始数据类型,只要值相等就返回true,否则返回false
  • 如果两个运算子值为复合类型,比较的是两个运算子的值是否指向同一个地址,如果地址相同,返回true,否则返回false
  • 特殊值nullunderfined与自身严格相等。
// 两个运算子值类型不同,直接返回false
1 === "1"; // false
true === "true"; // false
true === 1; // false
"1"===new String("1"); // false

// 两个运算子值类型相同,且为原始数据类型,值相等就返回true
true === true; // true
1 === 1; // true
"a" === "a"; // true

// 两个运算子为复合数据类型,比较的是地址是否一样
{} === {} // false
[] === [] // false

var x = new String("a");
y = x;
x === y // true

// 特殊值 nul,undefined与自身严格相等 
null === null;	
undefined === undefined ;

2.4 严格不相等运算符

严格不相等运算符(!==),它的算法就是先求严格相等运算符(===)的结果,然后返回相反值。

true !== "true"; // true,等价于 !(true === "true")

2.5 相等运算符

相等运算符(==)是二元运算符,用来比较时,会进行数据类型转换。 参与运算的运算子情况不同,也具有不同的结果:

  • 如果运算子数据类型一致,与严格相等运算符完全一样`。
  • 如果运算子数据类型不一致:
    • 都是原始类型,则会转换成数值进行比较。
    • 对象与原始类型,对象会转换为原始类型的值然后进行比较。
    • 特殊值nullNaNundefined
// 数据类型一致,与严格相等运算符一样
1 == 1; // true
true == true; // true
{} == {} // false
[] == [] // false


// 数据类型不一致
// 都是原始数据类型,先转换为数值再进行比较
1 == true; // true
0 == false; // true
1 == "1"; // true
'' == 0; // true
'' == false;  // true

// 对象与原始数据类型比较,对象会先转为原始数据类型然后再进行比较
var person = {
	name: "jidi",
	sex: "男",
	valueOf: function(){
		return 1;
	},
	toString: function(){
		return "jidi";
	}
}
//对象与数值比较,对象转换为数值然后进行比较
person == 1; // true

person.valueOf = function(){return {}};// 自定义valueOf方法返回一个空对象
// 对象与字符串比较,对象转换为字符串然后进行比较
person == "jidi"; // true

// 对象与布尔值比较,对象与布尔值都转换为数值然后进行比较
[1] == true; // true
[] == false; // true


// 特殊值null,NaN,undefined
// NaN与任何值(包括本身),都为false
NaN == true; // false
NaN == 1; // false
NaN == null; //false
NaN == undefined; // false
NaN == NaN; // false
NaN == ""; // false

//null与undefined与其他值进行比较时,都会false,只有相互比较或者自身比较时返回true
null == 0;
null == false;
null == "null";
null == "";
null == null;

undefined == 0;
undefined == "0";
undefined == ""; 
undefined == "undefined";
undefined == false;
undefined == undefined;

null == undefined;

注意:由于相等运算符(==)会进行数据类型转换,可能会出现一些违反常理的结果:

"" == 0; // true
false == "0"; // true
5 == true;    // false
5 == false;   // false

2.6 不相等运算符

相等运算符(==)有一个对应的“不相等运算符”(!=),它的算法就是先求相等运算符的结果,然后返回相反值。

3. 布尔值运算符

布尔值运算符用于将表达式转为布尔值,一共有四个运算符。

  • !,取反运算符
  • &&,且运算符
  • ||,或运算符
  • ?:,三元运算符

3.1 取反运算符

取反运算符用于将布尔值变成相反值,即原来为true取反后变为false,原来为false取反后为true
对于非布尔值,取反运算符会将其转换为布尔值。

!true; // false
!false; // true

// 非布尔值取反
!undefined // true
!null // true
!0 // true
!NaN // true
!"" // true

![] // false
!{} // false

数据类型转换的时候,以下几个值对它们进行取反为true,其它的值取反都为false

  • undefined
  • null
  • ’ ’
  • NaN
  • 0
  • false

3.2 且运算符

且运算符(&&)是二元运算符。且运算符(&&)的运算规则:如果第一个运算子布尔值true,返回第二个运算子的;如果第一个运算子布尔值false,直接返回第一个运算子的,且不再对第二个运算子进行求值,即会出现短路现象。

true && false; // false
5 > 3 && 200; // 200
var x = 1;
23>12 && x++; //2, x=2

false && true; // false
3>5 && 200; // false

var y = 20;
// 发生短路现象
0 && y--; // 0,y=20

且运算符(&&)可以多个连用,此时返回第一个布尔值为false的表达式的;如果表达式的布尔值都为true,则返回最后一个表达式的

// 返回第一个布尔值为false的表达式的值
1 && true && null && "example"; // null
1 && 0 && null && "example"; // 0

//表达式布尔值全为true,返回最后一个表达式的值
1 && true && 23 && "example"; // "example"

3.3 或运算符

或运算符(||) 也是二元运算符。或运算符(||)的运算规则:第一个运算子的布尔值为true,则返回第一个运算子的,且不会对第二个运算子进行计算,即发生短路现象;如果第一个运算子的布尔值为false,则返回第二个运算子的

1 || 2; // 1

//发生短路
var x = 3;
3 > 2 || x++; // true,x=3

3 < 2 || x++; // 4,x=4

利用或运算符(||) 的短路现象,可以给函数参数设置默认值。

function f(x){
	x = x || 5; // 如果x没有传值,则默认x=5
	return x;
}

//  调用函数
f(); // 此时返回默认值5

或运算符(||) 可以多个连用,这时返回第一个布尔值为true的表达式的值。如果所有表达式都为false,则返回最后一个表达式的值。

// 返回第一个布尔值为true的表达式的值
false || 0 || '' || 4 || 'foo' || true; // 4

// 所有表达式的布尔值为false,返回最后一个表达式的值
false || 0 || '' || null; // null

3.4 三元运算符

三元运算符是JavaScript唯一一个需要三个运算子的运算符。三元运算符的规则是:如果第一个运算子的布尔值为true,返回第二个运算子的值,否则返回第三个运算子的值。

true ? 1 : 2; // 1
false ? 1 : 2; // 2

4. 二进制位运算符

JavaScript中提供了以下位运算符:

  • |,二进制或运算符
  • &,二进制与运算符
  • ~,二进制否运算符
  • ^,异或运算符
  • <<,左移运算符
  • >>,右移运算符
  • >>>,头部补零的右移运算符

位运算符只对整数起作用,如果一个运算子不是整数,会自动转为整数后再执行。另外,在 JavaScript 内部,数值是以64位浮点数的形式储存的,但是在参与位运算的时候,是以32位带符号的整数进行运算的,并且返回值也是一个32位带符号的整数

4.1 二进制或运算符

二进制或运算符(|),逐位比较运算子,两个二进制位中只要有一个为1,就返回1,否则返回0

0 | 4; // 4

上面例子中,04的的二进制形式分别为:000100(只截取了部分,完整的是一个32位的二进制数字),二进制或运算结果为:100,即4。

小数参与位运算时,小数部分会舍去,只保留整数部分参与位运算。所以二进制或运算的一个常用场景是小数取整。

// 定义一个函数,获得整数部分的值
function getIntNumber(x){
	return x | 0;
}

// 调用该函数
getIntNumber(2.3); // 2
getIntNumber(-2.9); // 2

4.2 二进制与运算符

二进制与运算符(&),逐位比较两个运算子,两个二进制位之中只要有一个位为0,就返回0,否则返回1

2 & 3; // 1

4.3 二进制否运算符

二进制否运算符(~)将每个二进制位都变为相反值(0变为11变为0)。

~ 2; // -3

从上面例子看到,一个整数进行二进制否运算,结果并不是简单的改变值正负情况,这是因为计算机内部是使用补码来存储负数的。负数按位取反然后加1就得到补码。
简单的记法就是,一个数与自身的取反值相加等于-1

4.4 异或运算符

异或运算符(^)在两个二进制位不同时返回1,相同时返回0

0 ^ 3; // 3

上述代码中,03的二进制表示为0011,异或的结果为11,即为3。

我曾经碰到过一个面试题,在不引入新变量的前提下,交换变量ab的值。在这里就可以用异或解决。

var a = 3;
var b = 4;
a ^= b;
b ^= a;
a ^= b;
a; // 4
b; // 3

4.5 左移运算符

左移运算符(<<)表示将一个数的二进制值向左移动指定的位数,尾部以0填充,向左移动的时候,最高位的符号位是一起移动的。左移n位,相当于乘以2n次方。
如果左移0位,就相当于将数值转为32位整数,也可以取的一个数的整数部分。

4.6 右移运算符

右移运算符(>>)表示将一个数的二进制值向右移动指定的位数。如果是正数,头部全部补0;如果是负数,头部全部补1

4.7 头部补零的右移运算符

头部补零的右移运算符(>>>)与右移运算符(>>)只有一个差别,就是一个数的二进制形式向右移动时,头部一律补零,而不考虑符号位。

5. 其它运算符

5.1 void运算符

void运算符的作用是执行一个表达式,返回undefined

void(7 +7 ); // undefined

void运算符可以在超链接中插入代码但是不会发生网页跳转。

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

5.2 逗号运算符

逗号运算符()用于对两个表达式求值,并返回后一个表达式的值。

"x", "y"; // "y"

var x = 0;
var y = (x++, 10);
x // 1
y // 10

6. 参考链接

本博客是自己学习笔记,原文请参考JavaScript教程
如有问题,请及时指出!
欢迎沟通交流,邮箱:[email protected]

发布了15 篇原创文章 · 获赞 3 · 访问量 3662

猜你喜欢

转载自blog.csdn.net/qq_41863849/article/details/103978689