JavaScript——(2)

时间:2024-11-07 07:45:52

三、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';      // false
1 == 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 && ba 为真,那么该表达式的值由 b 决定(计算 a 又计算 b)

  • 如果 a && ba 为假,那么该表达式的值由 a 决定(只计算 a)

3 && 6;				// 6
undefined && 15;	// undefined
15 && undefined;	// undefined
null && 2;			// null
'' && 16;			// ''
NaN && undefined;	// NaN

(2)|| 短路运算

由于 || 运算的核心是:“全假为假、有真即真”,所以:

  • 如果 a || ba 为真,那么该表达式的值由 a 决定(只计算 a)

  • 如果 a || ba 为假,那么该表达式的值由 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 3
var 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 是一个标签,可以配合 breakcontinue 使程序跳转到这个标签处执行(执行 breakcontinue),从而改变程序的执行流程。

// 注意: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
*/