03月07, 2019

JS基础(2)——基本语法(4)——运算符

任何编程语言基本上都离不开运算符。在 JavaScript 中也是支持众多运算符的。例如最常见的算数运算符,比较运算符,逻辑运算符等。接下来,就让我们一起来看一下 JavaScript 中这几种常见的运算符。

2-4-1 算数运算符

常见的算数运算符有加减乘除和取模运算。主要需要注意的就是,在动态语言中做除法运算时,能够得到小数。下面是关于算数运算符的示例:

console.log(5 + 3.14); // 8.14
console.log(6 - 11); // -5
console.log(7 * 3); // 21
console.log(5 / 2); // 2.5
console.log(10 % 3); // 1

从 ECMAScript 6 开始新增加了求幂运算,使用两个*号代表求幂。以此可以代替以前的Math.pow()方法

console.log(2 ** 3); // 8

2-4-2 一元运算符

所谓一元运算符,就是指只作用于一个操作数的运算符。常见的一元运算符有两种,赋值运算符和递增递减运算符。

1. 赋值运算符

关于赋值运算符,前面我们已经见到过了。最常见的就是=,代表将右边的内容赋值给左边。除此之外,还有+=-=*=等一系列赋值运算符,具体的示例如下:

let a = 5;
a += 5;
console.log(a); // 10
a -= 3;
console.log(a); // 7
a *= 5;
console.log(a); // 35
a /= 5;
console.log(a); // 7
a %= 2;
console.log(a); // 1

2. 递增和递减

除了上面所介绍的赋值运算符以外,常见的一元运算符还有递增和递减。在递增递减中,主要需要注意前置和后置的区别。如果是前置,那么是先自增或自减,然后参与运算。如果是后置,则是先参与运算,然后再自增或者自减,示例如下:

前置示例:

let a = 2;
let b = 10;
let c = --a + b;
let d = a + b;
console.log(a,b,c,d); // 1 10 11 11

后置示例:

let a = 2;
let b = 10;
let c = a-- + b;
let d = a + b;
console.log(a,b,c,d); // 1 10 12 11

需要注意的是,我们的自增自减操作符不仅仅局限于数值,其他类型也可以,遵循下面的规则:

  • 在应用于一个包含有效数字字符的字符串时,现将其转换为数字值,再执行加减 1 操作。字符串变量变为了数值变量。
  • 在应用于一个不包含有效数字字符的字符串时,将变量的值设置为 NaN,字符串变量变成数值变量。
  • 遇布尔值 false 时,先将其转换为 0 再执行加减 1 操作,布尔值变量变成数值变量。
  • 遇布尔值 true 时,先将其转换为 1 再执行加减 1 操作,布尔值变量变成数值变量。
  • 在应用浮点数数值时,执行加减 1 操作。

示例:

let s1 = "123";
let s2 = "123Hello";
let s3 = "Hello";
let s4 = true;
let s5 = 3.14;
console.log(--s1); // 122
console.log(--s2); // NaN
console.log(--s3); // NaN
console.log(--s4); // 0
console.log(--s5); // 2.14

2-4-3 比较运算符

1. 关系运算符

常见的关系运算符有小于大于小于等于大于等于,关于数的比较就不用说了,如下:

console.log(5 > 3); // true
console.log(3 > 5); // false

主要说一下字符串的比较,如下:

console.log("c" > "b"); // true
console.log("c" > "box"); // true

这里的比较主要是按照 ASCII 码来进行比较的。

如果是字符串和数字进行比较,那么会将字符串先转换为数字,如果不能转换为数字,则转换为 NaN

console.log("5" > 3); // true,因为"5"转为了5
// 任何一个数与 NaN 进行关系比较,返回的都是 false
console.log("Hello" > 3); // false,因为"Hello"转为了NaN

完整的特殊规则如下:

  • 如果两个数都是数值,则执行数值比较
  • 如果两个数都是字符串,则比较两个字符串对应的字符编码
  • 如果一个操作数是数值,则将另一个操作数转换为一个数值,然后执行数值的比较
  • 如果一个操作数是对象,则调用这个对象的valueOf()方法,用得到的结果按照前面的规则执行比较。如果对象没有valueOf()方法,则调用toString()方法,并用得到的结果根据前面的规则执行比较。
  • 如果一个数是布尔值,则先将其转换为数值,然后再进行比较

还需要注意,任何数和 NaN 进行比较返回的都是 false

console.log(10 > NaN); // false
console.log(10 < NaN); // false

2. 相等和不相等

==表示相等,!=表示不相等,数据类型不同的数据进行相等比较的话会自动转换数据类型,还有一些其他的转换规则如下:

  • null 和 undefined 是相等的
  • 如果有一个操作数是 NaN,则返回 false,NaN 和 NaN 比较也是 false
  • 如果是数字的字符串和数字进行比较,会先将字符串转换为数字
  • 布尔值里面 true 转换为 1,false 转换为 0

下表列出了一些特殊的情况

表达式
null == undefined true
"NaN" == NaN false
5 == NaN false
NaN == NaN false
NaN != NaN true
false == 0 true
true == 1 true
true == 2 false
undefined == 0 false
null == 0 false
"5" == 5 true

3. 全等和不全等

全等是===,不全等是!==,所谓全等,就是要求数据类型和数值都必须相等,如下:

console.log("5" == 5); // true
console.log("5" === 5); // false

2-4-4 逻辑运算符

1. 非

所谓非,就是取反,非真即假,非假即真

let i = true;
console.log(!i); // false

非运算符不仅仅只能用于布尔值,其他数据类型也是可以的,如下:

  • 如果操作数是一个对象,返回 false
  • 如果操作数是一个空字符串,返回 true
  • 如果操作数是一个非空字符串,返回 false
  • 如果操作数是数值 0,返回true
  • 如果操作数是任意非 0 数值(包括 Infinity),返回 false
  • 如果操作数是 null,返回 true
  • 如果操作数是 NaN,返回 true
  • 如果操作数是 undefined,返回 true
console.log(!false); // true
console.log(!"blue"); // false
console.log(!0); // true
console.log(!NaN); // true
console.log(!""); // true
console.log(!12); // false

可使用双否定!!来判定一个值是真值还是假值,如下:

console.log(!!''); // false
console.log(!!NaN); // false
console.log(!!'Hello'); // true

2. 与

作用于两到多个值,并且只有所有的操作数都是真值时,才为 true

console.log(false && true); // false
console.log(true && true); // true

JavaScript里面的&&存在短路现象,具体说明如下:

  • 第一个操作数为真:会进入第二个操作数的判断,且无论第二个操作数真假,都会返回第二个操作数。
  • 第一个操作数为假:不会进入第二个操作数的判断,直接返回第一个操作数。

来看下面的例子:

console.log(3 && 5); // 5
console.log("Hello" && 20); // 20
console.log("Hello" && false); // false
console.log("" && "shoe"); // ""
console.log("Hello" && ''); // ''

下面是关于&&运算符的一道经典练习题:

let a = true;
let b = a && c; // 因为a是true,所以会判断第2个数
console.log(b);
// ReferenceError: c is not defined
let a = false;
let b = a && c; // 因为a是false,所以不会判断第2个数
console.log(b); // false

如果你看《JavaScript高级程序设计》这本书,里面还讲了诸如下面这些杂七杂八的规则:

  • 如果第一个操作数是对象,则返回第二个操作数
  • 如果第二个操作数是对象,则只有在第一个操作数的求值结果为 true 的情况下才会返回该对象
  • 如果两个操作数都是对象,则返回第二个操作数
  • 如果有一个操作数是 null,则返回 null
  • 如果有一个操作数是 NaN,则返回 NaN
  • 如果有一个操作数是 undefined,则返回 undefined
console.log(3 && 5); // 5
console.log(NaN && NaN); // NaN
console.log(null && null); // null
console.log(undefined && undefined); // undefined

但是这些规则大多都是可以使用短路现象来解释的,所以不需要你去记,大致了解即可。

3. 或

同样是作用于两到多个值,但是只要有一个操作数为真,就返回真

console.log(false || true); // true
console.log(true || false); // true

JavaScript 里面的||同样存在短路现象,具体说明如下:

  • 如果第一个操作数为真,则不会进入第二个数的判断。所以无论第二个操作数真假,都直接返回第一个操作数
  • 如果第一个操作数为假,则会进入第二个数的判断。但是无论第二个操作数真假,都直接返回第二个操作数

来看下面的例子:

console.log(false || true); // true
console.log("Hello" || ""); // Hello
console.log("Hello" || "str"); // Hello
console.log(NaN || ""); // ""
console.log(0 || "Hello World"); // Hello World
console.log('' || 'str'); // str
console.log('' || false); // false

下面是关于||运算符的一道经典练习题:

let a = false;
let b = a || c; // 因为a是false,所以会判断第2个数
console.log(b);
// ReferenceError: c is not defined
let a = true;
let b = a || c; // 因为a是false,所以会判断第2个数
console.log(b); // true

如果你看《JavaScript高级程序设计》这本书,里面也讲了诸如下面这些杂七杂八的规则:

  • 如果第一个操作数是对象,则返回第一个操作数
  • 如果第一个操作数的求值结果为 false,则返回第二个操作数
  • 如果两个操作数都是对象,则返回第一个操作数
  • 如果两个数都是 null,则返回 null
  • 如果两个数都是 NaN,则返回 NaN
  • 如果两个数都是 undefined,则返回 undefined
console.log(3 || 5); // 3
console.log(NaN || NaN); // NaN
console.log(null || null); // null
console.log(undefined || undefined); // undefined

不过这些规则大多也都是可以使用短路现象来解释的,所以根本不需要你去记,大致了解即可。

2-4-5 位运算符(扩展)

按位运算符是将操作数换算成32位的二进制整数,然后按每一位来进行运输。例如:

5 的32位为:

00000000000000000000000000000101

100 的32位为:

00000000000000000000000001100100

15 的32位为:

00000000000000000000000000001111

1. 按位非

按位非运算符~会把数字转为32位二进制整数,然后反转每一位。所有的 1 变为 0,所有的 0 变为 1

例如:

5 的 32 位为:

00000000000000000000000000000101

~5 的 32 位为:

11111111111111111111111111111010  

转换出来就为 -6

按位非,实质上是对操作数求负,然后减去1。

2. 按位与

按位或运算符&会把两个数字转为 32 位二进制整数,并对两个数的每一位执行按位与运算。按位与的规则如下表:

第一个数字 第二个数字 结果
1 1 1
1 0 0
0 1 0
0 0 0

具体示例:

console.log(12 & 10); // 8

12 的 32 位二进制表示为:1100
10 的 32 位二进制表示为:1010

按位与的结果为:1000

3. 按位或

按位或运算符|会把两个数字转为 32 位二进制整数,并对两个数的每一位执行按位或运算。按位或的规则如下表:

第一个数字 第二个数字 结果
1 1 1
1 0 1
0 1 1
0 0 0

具体示例:

console.log(12 | 10); // 14

12 的 32 位二进制表示为:1100
10 的 32 位二进制表示为:1010

按位或的结果为:1110

4. 按位异或

按位或运算符^会把两个数字转为 32 位二进制整数,并对两个数的每一位执行按位异或运算。运算规则为两位不同返回 1,两位相同返回 0,如下表:

第一个数字 第二个数字 结果
1 1 0
1 0 1
0 1 1
0 0 0

具体示例:

console.log(12 ^ 10); // 6

12 的 32 位二进制表示为:1100
10 的 32 位二进制表示为:1010

按位异或的结果为:0110

按位异或如果是非整数值,如果两个操作数中只有一个为真,就返回 1,如果两个操作数都是真,或者都是假,就返回 0,示例如下:

console.log(true ^ "Hello"); // 1
console.log(false ^ "Hello"); // 0
console.log(true ^ true); // 0
console.log("Hello" ^ "Hello"); // 0
console.log(false ^ false); // 0
console.log(true ^ false); // 1

注意这里的 Hello 被转换为了 NaN

5. 按位移位

按位移位运算符<<>>会将所有位向左或者向右移动指定的数量,实际上就是高效率地将数字乘以或者除以 2 的指定数的次方。

<<:乘以 2 的指定数次方

console.log(2<<2); // 8

2 乘以 2 的 2 次方

00000010 转换为 00001000

>>:除以 2 的指定数次方

console.log(16>>1); // 8

16 除以 2 的 1 次方

00010000转换为00001000

2-4-6 运算符优先级

JavaScript 中的运算符优先级是一套规则。该规则在计算表达式时控制运算符执行的顺序。具有较高优先级的运算符先于较低优先级的运算符执行。例如,乘法的执行先于加法。

下表按照最高到最低的优先级列出了 JavaScript 运算符。具有相同优先级的运算符按从左至右的顺序求值。

运算符 描述
. [] () 字段访问,数组下标,函数调用以及表达式分组
++ -- - ~ ! delete new typeof void 一元运算符,返回类型,对象创建,未定义值
* / % 乘法,除法,取模
+ - + 加法,减法,字符串拼接
<< >> >>> 移位
< <= > >= instanceof 小于,小于等于,大于,大于等于,instanceof
== != === !== 等于,不等于,全等,不全等
& 按位与
^ 按位异或
| 按位或
&& 逻辑与
|| 逻辑或
?: 三目运算符
= 赋值
, 多重赋值

总结

  1. 拥有良好注释的代码是专业程序员的标志。如果没有注释,往往在几周后自己也很难明白自己写的代码的含义。

  2. 在 JavaScript 中,每一条代码以分号结束。

  3. 标识符就是我们自己给变量。函数或者对象起的一个名字。定义标识符时需要遵循一定的硬性要求和软性要求。

  4. 常见的命名法则有匈牙利命名法,驼峰命名法和蛇形命名法。

  5. 在 JavaScript 中存在一组关键字和保留字,不能用作自定义标识符。

  6. 在 JavaScript 中数据类型整体上可以分为简单数据类型和复杂数据类型。

  7. 使用 typeof 运算符可以查看一个数据的数据类型。

  8. 在 JavaScript 中声明变量可以使用 let,const 和 var 关键字来进行声明。

  9. 简单值和复杂值在访问方式,比较方式以及内存中存储空间的区别。

  10. 使用 var 关键字声明变量存在一些特殊的特性,例如重复声明以及遗漏声明

  11. 所谓作用域就是指变量在程序中能够被访问到的区域。常见的有全局作用域和局部作用域。

  12. JavaScript 中存在 6 种基本数据类型,分别为 undefined,null,boolean,number,string和 symbol。

  13. 每种数据类型都有其对应的属性和方法。掌握这些属性和方法可以让我们在编写代码时事半功倍。

  14. 不同的数据类型之间可以相互进行转换,其中分为隐性转换和强制转换

  15. JavaScript 中有众多的运算符,例如最常用的算数运算符,还有比较运算符,逻辑运算符,和位运算符等。不同的运算符之间有一套优先级关系。

本文链接:http://www.yanhongzhi.com/post/js-basis-5.html

-- EOF --

Comments