三、JS表达式与操作符
1. 什么是表达式和运算符
表达式:由 操作数 和 运算符 组成的式子。
表达式的分类:算术、关系、逻辑、赋值、综合。
2. 算术表达式
2.1 算术运算符
意义 | 运算符 |
---|---|
加 | + |
减 | - |
乘 | * |
除 | / |
取余 | % |
推荐一律使用
()
来强制规定优先级!
+
有 “加法” 和 “连字符” 两种作用,如果 +
的某一边是字符串那么就为 ”连字符“,否则为 ”加法“。
2.2 隐式类型转换
如果参与数学运算的某操作数不是数字类型,那么 JS 会自动将此操作数转换为数字型。
除“+”运算符外,其余运算符操作会将其他类型的操作数转换为数字类型。
3 * '4'; // 12 true + true; // 2 false + 2; // 2 3 * '2天'; // NaN "z" * "j"; // NaN 3 + '2天'; // '32天' 3 + null; // 3 3 * ''; // 0 3 * ' '; // 0 3 + ''; // '3' 3 + ' '; // '3 '
隐式转换的本质是 JS 内部自动调用 Number() 函数
2.3 幂和开根号
JS 中没有提供幂运算、开根号的运算符,需要使用 Math 对象的相关方法进行计算。
Math.pow(a, b)
:求 a 的 b 次方。
Math.sqrt(a)
:求 a 的平方根。
Math.pow(2, 3); // 8 Math.pow(3, 2); // 9 Math.sqrt(81); // 9 Math.sqrt(-81); // NaN
2.4 向上取整和向下取整
Math.ceil()
:向上取整。
Math.floor()
:向下取整。
Math.round()
:把一个数字舍入为最接近的整数(“四舍六入”,“五不一定”)
注意:向上、向下的标准是:X轴正方向为上!
负 ———— 0 ————> 正
Math.ceil(2.4); // 3 Math.floor(2.4); // 2 Math.ceil(-2.4); // -2 Math.floor(-2.4); // -3 Math.ceil(2); // 2 Math.floor(2); // 2
注意:由于底层的关系,无论是
toFixed()
还是Math.round()
都不是完全遵循 “四舍五入”(无论正负),而是 “四舍六入,五不一定”,为什么非要这样呢?原因是没办法!因为小数在计算机底层的实现机制导致的问题!建议对于小数位数的处理不要用原生的函数来处理!最好是手动实现想要的处理代码,或者是借助第三方库!
3. 关系表达式
3.1 关系运算符
意义 | 运算符 |
---|---|
大于 | > |
小于 | < |
大于或等于 | >= |
小于或等于 | <= |
等于 | == |
不等于 | != |
全等于 | === |
不全等于 | !== |
3.2 相等和全等
两个等号 ==
运算符,不考虑值的类型,它会进行隐式转换后比较值的字面量是否相等。
三个等号 ===
运算符,不仅比较值是否相同,同时比较类型是否相同。
5 == '5'; // true 5 === '5'; // false1 == true; // true 1 === true; // false 0 == false; // true 0 === false; // false 0 == undefined; // false 0 === undefined; // false undefined == null; // true undefined === null; // false
null 和 undefined 用 == 进行比较涉及隐式强制类型转换,ES5 规范中规定。
=== 比较为 false,是因为 null 与 undefined 类型不同。
建议没有特殊情况请一律使用 ===
3.3 NaN不自等
NaN 作为一个特殊的数字类型值,它在用 ==
比较的时候也有特殊的结果。
NaN == NaN; // false NaN === NaN; // false
【如何判断某变量值为 NaN】
isNaN()
函数可以用来判断变量值是否为 NaN。
isNaN(NaN); // true isNaN(5); // false isNaN('5'); // false
但 isNaN() 也不好用,它的机理是:只要该变量传入 Number() 的执行结果是 NaN,则 isNaN() 函数都会得到 true。
对于,undefined 和 null,这种情况一般来说需要先进行单独判断,再进行 isNaN 判断。
isNaN(undefined); // true isNaN('3天'); // true isNaN(null); // false
3.4 JS中没有连比
例如:3 <= a <= 15
的写法是错误的,应该为:a >= 3 && a <= 15
。
4. 逻辑表达式
4.1 逻辑运算符
意义 | 运算符 |
---|---|
非 | ! |
与 | && (短路运算符) |
或 | || (短路运算符) |
4.2 非运算
!
表示 “非”,也可以称为 “置反运算”。
!
是一个 “单目运算符”,只需要一个操作数。
置反运算的结果一定是布尔值。
!true; // false !false; // true !0; // true !undefined; // true !''; // true !' '; // false !null; // true !'imooc'; // false
!!
常用于确定一个值的布尔属性。!!true; // true !!0; // false !!''; // false !!' '; // true !!'imooc'; // true
4.3 与运算
&&
是 “双目运算符”。
核心:全真为真、有假即假
。
4.4 或运算
||
是 “双目运算符”。
核心:全假为假、有真即真
。
4.5 短路运算
&&
与 ||
,都属于 “短路运算符”。
(1)&&
短路运算
由于 &&
运算的核心是:“全真为真、有假即假”,所以:
-
如果
a && b
中a
为真,那么该表达式的值由b
决定(计算 a 又计算 b) -
如果
a && b
中a
为假,那么该表达式的值由a
决定(只计算 a)
3 && 6; // 6 undefined && 15; // undefined 15 && undefined; // undefined null && 2; // null '' && 16; // '' NaN && undefined; // NaN
(2)||
短路运算
由于 ||
运算的核心是:“全假为假、有真即真”,所以:
-
如果
a || b
中a
为真,那么该表达式的值由a
决定(只计算 a) -
如果
a || b
中a
为假,那么该表达式的值由b
决定(计算 a 又计算 b)
3 || 6; // 3 0 || 6; // 6 null || undefined; // undefined 'a' || 'b'; // 'a' NaN || null; // null
4.6 逻辑运算的优先级
优先级:!
> &&
> ||
!true || true; // true 3 && 4 || 5 && 6; // 4
推荐使用
()
来规定优先级。
5. 赋值表达式
5.1 赋值运算符
意义 | 运算符 |
---|---|
赋值 | = |
快捷赋值 | += 、-= 、*= 、/= 、%= |
自增运算 | ++ |
自减运算 | -- |
5.2 赋值运算产生值
赋值运算也产生值,赋值号后面的值将作为 “赋值运算表达式的值”。
var a; console.log(a = 4); // 4
这就意味着,可以连续使用赋值运算符。
var a, b, c; a = b = c = 15; console.log(a); // 15 console.log(b); // 15 console.log(c); // 15
在实际开发中不建议使用连续赋值!
5.3 自增自减运算
a++
:先用再加;++a
:先加再用。
a--
:先用再减;--a
:先减再用。
6. 综合运算的运算顺序
非运算 > 数学运算 > 关系运算 > 逻辑运算
建议使用
()
来规定优先级。
四、JS流程控制
1. if 条件语句
if (测试条件) { }if (测试条件) { } else { }if (测试条件) { } else if (测试条件) { } else { }
-
条件语句可以嵌套(最好不要嵌套超过三层)
-
if-elseif-else 语句注意条件的区间(下一级条件的成立是建立在上一级条件不成立的前提下)
-
可以只有 if 和 else if
2. switch 选择语句
switch (变量/表达式) { case 常量值/变量/表达式: 语句; break; case 常量值/变量/表达式: 语句; break; default: 语句; }
-
与其他高级语言不同,在 JS 中 case 后不仅仅只能跟常量值,还可以跟变量和表达式
-
注意 switch 语句的 “开关” 特性(遇见 break 才跳出 switch,否则直接进入下一个 case),合理运用好 break(例如不加 break 可以实现多条 case 共用同一个语句体)
-
default 语句不是必须的
3. 三元运算符
条件表达式 ? 表达式1 : 表达式2;
当条件表达式为真时执行 表达式1 并返回结果,否则执行 表达式2 并返回结果。
【三元运算符的用途】
根据某个条件是否成立,在两个不同值中选择最终值。
var age = 24; var type = age >= 18 ? '成年人' : '未成年人'; alert(type);
4. for 循环语句
for (初次表达式; 判断条件; 历次表达式) { }
在 JS 中,支持在 “初次表达式” 中声明变量并赋值。
【执行过程】
-
先执行 “初次表达式”(只唯一执行一次)
-
判断条件(若条件为 false,退出循环)
-
执行语句块
-
执行 “历次表达式”
-
判断条件(若条件为 false,退出循环)
-
执行语句块
-
执行 “历次表达式”
-
判断条件(若条件为 false,退出循环)
-
……
【for ... in 循环】
for
循环的一个变体是 for ... in
循环,它可以把一个对象的所有属性依次循环出来:
其中:key
是字符串类型,值为对象的属性名。
var o = { name: 'Jerry', age: 20, city: 'Beijing' }; for (var key in o) { console.log(key + ': ' + o[key]); } /* "name: Jerry" "age: 20" "city: Beijing" */
要过滤掉对象继承的属性,用 hasOwnProperty()
来实现:
var o = { name: 'Jerry', age: 20, city: 'Beijing' }; for (var key in o) { if (o.hasOwnProperty(key)) { console.log(key + ': ' + o[key]); } } /* "name: Jerry" "age: 20" "city: Beijing" */
由于数组也是对象的一种,因此,for ... in
循环可以直接循环出数组的索引:
其中:i
是字符串类型,值为数组的索引值(字符串类型)。
var a = ['A', 'B', 'C']; for (var i in a) { console.log(i + ': ' + a[i]); } /* 0: A 1: B 2: C */
再次提醒:for ... in
对数组的循环得到的索引是 String
而不是 Number
。
【for ... of 循环】
for in
更适合遍历对象,当然也可以遍历数组,但是会存在一些问题,例如:索引为字符串型数字,不能直接进行几何运算!某些情况下,遍历顺序有可能不是按照实际数组的内部顺序!
使用 for in
会遍历数组所有的可枚举属性,包括原型,如果不想遍历原型上的方法和属性的话,可以在循环内部判断一下,使用 hasOwnProperty()
方法可以判断某属性是不是该对象的实例属性:
var arr = [1, 2, 3]; Array.prototype.n = 123; for (var i in arr) { var res = arr[i]; console.log(res); } // 1 2 3 123 for(var i in arr) { if(arr.hasOwnProperty(i)) { var res = arr[i]; console.log(res); } } // 1 2 3
ES6 中,引入了 for ... of
循环!适用遍历 数组/字符串/map/set 等拥有迭代器对象(iterator)的集合,但是不能遍历普通对象!如果想遍历对象的属性,你可以用 for in
循环(这也是它的本职工作)或用内建的 Object.keys()
方法(获取对象的实例属性组成的数组,不包括原型方法和属性)……
for of
遍历的是数组元素的值,而且 for of
遍历的只是数组内的元素,不包括原型属性和索引!
var arr = [1, 2, 3]; Array.prototype.a = 123; for (var value of arr) { console.log(value); } // 1 2 3var str = '13579'; for (var c of str) { console.log(c); } // 1 3 5 7 9
推荐:遍历普通对象用:for in
,遍历数组及字符串等用:for of
。
5. while 循环语句
while (判断条件) { }do { } while (判断条件);
在 while 中,先判断条件,条件满足时再执行语句体。
在 do-while 中,do 内的语句块先执行一次,再判断条件。
6. break 和 continue
break;
:立即终止本层次循环。
continue;
:立即跳过本层次循环,提前进入本层次的下一次循环。
7. label 表达式
label
是一个标签,可以配合 break
或 continue
使程序跳转到这个标签处执行(执行 break
或 continue
),从而改变程序的执行流程。
// 注意:label 不是一个特定的关键字,可以随便取名 label: for (var i = 0; i < 10; i++) { for (var j = 0; j < 10; j++) { if (i + j === 6) { console.log("j=" + j); break label; } } console.log("i=" + i); } /* j=6 */label: for (var i = 0; i < 10; i++) { for (var j = 0; j < 10; j++) { if (i + j === 6) { console.log("j=" + j); continue label; } } console.log("i=" + i); } /* j=6 j=5 j=4 j=3 j=2 j=1 j=0 i=7 i=8 i=9 */// label + break 配合可以用在循环外的 if 语句中 label: { if (1 > 0) { console.log("1"); break label; } console.log("2"); } /* 1 */