JS 运算符和流程控制
一、操作符
1.一元操作符
自加自减操作符
前置型
执行前置递增和递减操作时,变量的值都是在语句被求值以前改变的
var num1 = 2; var num2 = 20; var num3 = --num1 + num2; // 等于 21 var num4 = num1 + num2; // 等于 21
后置型
把递增操作符放在变量后面并不会改变语句的结果
var num1 = 2; var num2 = 20; var num3 = num1-- + num2; // 等于 22 var num4 = num1 + num2; // 等于 21
在应用于一个包含有效数字字符的字符串时,先将其转换为数字值,再执行加减 1 的操作。字
符串变量变成数值变量。
在应用于一个不包含有效数字字符的字符串时,将变量的值设置为 NaN(第 4 章将详细讨论)。
字符串变量变成数值变量。
在应用于布尔值 false 时,先将其转换为 0 再执行加减 1 的操作。布尔值变量变成数值变量。
在应用于布尔值 true 时,先将其转换为 1 再执行加减 1 的操作。布尔值变量变成数值变量。
在应用于浮点数值时,执行加减 1 的操作。
在应用于对象时,先调用对象的 valueOf()方法(第 5 章将详细讨论)以取得一个可供操作的
值。然后对该值应用前述规则。如果结果是 NaN,则在调用 toString()方法后再应用前述规
则。对象变量变成数值变量
var s1 = "2"; var s2 = "z"; var b = false; var f = 1.1; var o = { valueOf: function() { return -1; } }; s1++; // 值变成数值 3 s2++; // 值变成 NaN b++; // 值变成数值 1 f--; // 值变成 0.10000000000000009(由于浮点舍入错误所致) o--; // 值变成数值-2
加和减操作符
加
一元加操作符以一个加号(+)表示,放在数值前面,对数值不会产生任何影响,
var num = 25; num = +num; // 仍然是 25
不过,在对非数值应用一元加操作符时,该操作符会像 Number()转型函数一样对这个值执行转换
var s1 = "01"; var s2 = "1.1"; var s3 = "z"; var b = false; var f = 1.1; var o = { valueOf: function() { return -1; } }; s1 = +s1; // 值变成数值 1 s2 = +s2; // 值变成数值 1.1 s3 = +s3; // 值变成 NaN b = +b; // 值变成数值 0 f = +f; // 值未变,仍然是 1.1 o = +o; // 值变成数值-1
减
一元减操作符主要用于表示负数
var num = 25; num = -num; // 变成了-25
当应用于非数值时,一元减操作符遵循与一元加操作符相同的规则,最后再将得到的数值转换为负数
var s1 = "01"; var s2 = "1.1"; var s3 = "z"; var b = false; var f = 1.1; var o = { valueOf: function() { return -1; } }; s1 = -s1; // 值变成了数值-1 s2 = -s2; // 值变成了数值-1.1 s3 = -s3; // 值变成了 NaN b = -b; // 值变成了数值 0 f = -f; // 变成了-1.1 o = -o; // 值变成了数值 1
2.位操作符
暂略
3.布尔操作符
逻辑非(!)
无论这个值是什么数据类型,这个操作符都会返回一个布尔值。逻辑非操作符首先会将它的操作数转换为一个布尔值,然后再对其求反
如果操作数是一个对象,返回 false;
如果操作数是一个空字符串,返回 true;
如果操作数是一个非空字符串,返回 false;
如果操作数是数值 0,返回 true;
如果操作数是任意非 0 数值(包括 Infinity),返回 false;
如果操作数是 null,返回 true;
如果操作数是 NaN,返回 true;
如果操作数是 undefined,返回 true。
alert(!false); // true alert(!"blue"); // false alert(!0); // true alert(!NaN); // true alert(!""); // true alert(!12345); // false
同时使用两个逻辑非操作符,实际上就会模拟 Boolean()转型函数的行为
alert(!!"blue"); //true alert(!!0); //false alert(!!NaN); //false alert(!!""); //false alert(!!12345); //true
逻辑与(&&)
逻辑与操作可以应用于任何类型的操作数,而不仅仅是布尔值
如果第一个操作数是对象,则返回第二个操作数;
如果第二个操作数是对象,则只有在第一个操作数的求值结果为 true 的情况下才会返回该
对象;
如果两个操作数都是对象,则返回第二个操作数;
如果有一个操作数是 null,则返回 null;
如果有一个操作数是 NaN,则返回 NaN;
如果有一个操作数是 undefined,则返回 undefined。
如果第一个操作数是 false,则无论第二个操作数是什么值,结果都不再可能是true 了
var found = true; var result = (found && someUndefinedVariable); // 这里会发生错误 alert(result); // 这一行不会执行
var found = false; var result = (found && someUndefinedVariable); // 不会发生错误 alert(result); // 会执行( "false")
逻辑或
与逻辑与操作相似,如果有一个操作数不是布尔值,逻辑或也不一定返回布尔值
如果第一个操作数是对象,则返回第一个操作数;
如果第一个操作数的求值结果为 false,则返回第二个操作数;
如果两个操作数都是对象,则返回第一个操作数;
如果两个操作数都是 null,则返回 null;
如果两个操作数都是 NaN,则返回 NaN;
如果两个操作数都是 undefined,则返回 undefined
与逻辑与操作符相似,逻辑或操作符也是短路操作符。也就是说,如果第一个操作数的求值结果为
true,就不会对第二个操作数求值了
var found = true; var result = (found || someUndefinedVariable); // 不会发生错误 alert(result); // 会执行( "true")
4.乘性操作符
乘法
var result = 34 * 56;
在处理特殊值的情况下,乘法操作符遵循下列特殊的规则:
如果操作数都是数值,执行常规的乘法计算,即两个正数或两个负数相乘的结果还是正数,而
如果只有一个操作数有符号,那么结果就是负数。如果乘积超过了 ECMAScript 数值的表示范围,
则返回 Infinity 或-Infinity;
如果有一个操作数是 NaN,则结果是 NaN;
如果是 Infinity 与 0 相乘,则结果是 NaN;
如果是 Infinity 与非 0 数值相乘,则结果是 Infinity 或-Infinity,取决于有符号操作数
的符号;
如果是 Infinity 与 Infinity 相乘,则结果是 Infinity;
如果有一个操作数不是数值,则在后台调用 Number()将其转换为数值,然后再应用上面的
规则。
除法
var result = 66 / 11;
如果操作数都是数值,执行常规的除法计算,即两个正数或两个负数相除的结果还是正数,而
如果只有一个操作数有符号,那么结果就是负数。如果商超过了 ECMAScript 数值的表示范围,
则返回 Infinity 或-Infinity;
如果有一个操作数是 NaN,则结果是 NaN;
如果是 Infinity 被 Infinity 除,则结果是 NaN;
如果是零被零除,则结果是 NaN;
如果是非零的有限数被零除,则结果是 Infinity 或-Infinity,取决于有符号操作数的符号;
如果是 Infinity 被任何非零数值除,则结果是 Infinity 或-Infinity,取决于有符号操作
数的符号;
如果有一个操作数不是数值,则在后台调用 Number()将其转换为数值,然后再应用上面的规则。
求余
var result = 26 % 5; // 等于 1
与另外两个乘性操作符类似,求模操作符会遵循下列特殊规则来处理特殊的值:
如果操作数都是数值,执行常规的除法计算,返回除得的余数;
如果被除数是无穷大值而除数是有限大的数值,则结果是 NaN;
如果被除数是有限大的数值而除数是零,则结果是 NaN;
如果是 Infinity 被 Infinity 除,则结果是 NaN;
如果被除数是有限大的数值而除数是无穷大的数值,则结果是被除数;
如果被除数是零,则结果是零;
如果有一个操作数不是数值,则在后台调用 Number()将其转换为数值,然后再应用上面的规则
5.加性操作符
var result = 1 + 2;
如果有一个操作数是 NaN,则结果是 NaN;
如果是 Infinity 加 Infinity,则结果是 Infinity;
如果是-Infinity 加-Infinity,则结果是-Infinity;
如果是 Infinity 加-Infinity,则结果是 NaN;
如果是+0 加+0,则结果是+0;
如果是0 加0,则结果是0;
如果是+0 加0,则结果是+0。
不过,如果有一个操作数是字符串,那么就要应用如下规则:
如果两个操作数都是字符串,则将第二个操作数与第一个操作数拼接起来;
如果只有一个操作数是字符串,则将另一个操作数转换为字符串,然后再将两个字符串拼接
起来。
如果有一个操作数是对象、数值或布尔值,则调用它们的 toString()方法取得相应的字符串值,
然后再应用前面关于字符串的规则。对于 undefined 和 null,则分别调用 String()函数并取得字符
串"undefined"和"null"
var result1 = 5 + 5; // 两个数值相加 alert(result1); // 10 var result2 = 5 + "5"; // 一个数值和一个字符串相加 alert(result2); // "55"
var num1 = 5; var num2 = 10; var message = "The sum of 5 and 10 is " + num1 + num2; alert(message); // "The sum of 5 and 10 is 510"
var num1 = 5; var num2 = 10; var message = "The sum of 5 and 10 is " + (num1 + num2); alert(message); //"The sum of 5 and 10 is 15"
减法
var result = 2 - 1;
如果两个操作符都是数值,则执行常规的算术减法操作并返回结果;
如果有一个操作数是 NaN,则结果是 NaN;
如果是 Infinity 减 Infinity,则结果是 NaN;
如果是-Infinity 减-Infinity,则结果是 NaN;
如果是 Infinity 减-Infinity,则结果是 Infinity;
如果是-Infinity 减 Infinity,则结果是-Infinity;
如果是+0 减+0,则结果是+0;
如果是+0 减0,则结果是0;
如果是0 减0,则结果是+0;
如果有一个操作数是字符串、布尔值、 null 或 undefined,则先在后台调用 Number()函数将
其转换为数值,然后再根据前面的规则执行减法计算。如果转换的结果是 NaN,则减法的结果
就是 NaN;
如果有一个操作数是对象,则调用对象的 valueOf()方法以取得表示该对象的数值。如果得到
的值是 NaN,则减法的结果就是 NaN。如果对象没有 valueOf()方法,则调用其 toString()
方法并将得到的字符串转换为数值
var result1 = 5 - true; // 4,因为 true 被转换成了 1 var result2 = NaN - 1; // NaN var result3 = 5 - 3; // 2 var result4 = 5 - ""; // 5,因为"" 被转换成了 0 var result5 = 5 - "2"; // 3,因为"2"被转换成了 2 var result6 = 5 - null; // 5,因为 null 被转换成了 0
6.关系操作符
小于(<) 、大于(>) 、小于等于(<=)和大于等于(>=)再应用上面的规则。这几个操作符都返回一个布尔值
var result1 = 5 > 3; //true var result2 = 5 < 3; //false
如果两个操作数都是数值,则执行数值比较。
如果两个操作数都是字符串,则比较两个字符串对应的字符编码值。
如果一个操作数是数值,则将另一个操作数转换为一个数值,然后执行数值比较。
如果一个操作数是对象,则调用这个对象的 valueOf()方法,用得到的结果按照前面的规则执
行比较。如果对象没有 valueOf()方法,则调用 toString()方法,并用得到的结果根据前面
的规则执行比较。
如果一个操作数是布尔值,则先将其转换为数值,然后再执行比较。
在比较字符串时,实际比较的是两个字符串中对应位置的每个字符的字符编码值。经过这么一番比较之后,再返回一个布尔值
var result = "Brick" < "alphabet"; //true
var result = "23" < "3"; //true
在比较数值和字符串时,字符串都会被转换成数值,然后再以数值方式与另一个数值比较
var result = "23" < 3; //false
如果那个字符串不能被转换成一个合理的数值
var result = "a" < 3; // false,因为"a"被转换成了 NaN
任何操作数与 NaN 进行关系比较,结果都是 false
var result1 = NaN < 3; //false var result2 = NaN >= 3; //false
7.相等操作符
相等和不相等
如果两个操作数相等,则返回 true ,两个操作数不相等,则返回 false
两个操作符都会先转换操作数(通常称为强制转型),然后再比较它们的相等性
在转换不同的数据类型时的基本规则 :
如果有一个操作数是布尔值,则在比较相等性之前先将其转换为数值——false 转换为 0,而
true 转换为 1;
如果一个操作数是字符串,另一个操作数是数值,在比较相等性之前先将字符串转换为数值;
如果一个操作数是对象,另一个操作数不是,则调用对象的 valueOf()方法,用得到的基本类
型值按照前面的规则进行比较;
这两个操作符在进行比较时则要遵循下列规则。
null 和 undefined 是相等的。
要比较相等性之前,不能将 null 和 undefined 转换成其他任何值。
如果有一个操作数是 NaN,则相等操作符返回 false,而不相等操作符返回 true。重要提示:
即使两个操作数都是 NaN,相等操作符也返回 false;因为按照规则, NaN 不等于 NaN。
如果两个操作数都是对象,则比较它们是不是同一个对象。如果两个操作数都指向同一个对象,
则相等操作符返回 true;否则,返回 false
全等和不全等
除了在比较之前不转换操作数之外,全等和不全等操作符与相等和不相等操作符没有什么区别。全等操作符由 3 个等于号(===)表示,它只在两个操作数未经转换就相等的情况下返回 true
var result1 = ("55" == 55); //true,因为转换后相等 var result2 = ("55" === 55); //false,因为不同的数据类型不相等
var result1 = ("55" != 55); //false,因为转换后相等 var result2 = ("55" !== 55); //true,因为不同的数据类型不相等
*** null == undefined 会返回 true,因为它们是类似的值;但 null === undefined 会返回 false,因为它们是不同类型的值
8.条件操作符
var max = (num1 > num2) ? num1 : num2;
如果num1 > num2 为真,返回num1
如果num1 > num2 为假,返回num2
9.赋值操作符
如果在等于号(=)前面再添加乘性操作符、加性操作符或位操作符,就可以完成复合赋值操作
num += 10; //相当于 num = num + 10;
乘/赋值(*=);
除/赋值(/=);
模/赋值(%=);
加/赋值(+=);
减/赋值(=);
左移/赋值(<<=);
有符号右移/赋值(>>=)
无符号右移/赋值(>>>=)
10.逗号操作符
使用逗号操作符可以在一条语句中执行多个操作
var num1=1, num2=2, num3=3;
在用于赋值时,逗号操作符总会返回表达式中的最后一项
var num = (5, 1, 4, 8, 0); // num 的值为 0
虽然逗号的这种使用方式并不常见,但这个例子可以帮我们理解逗号的这种行为
二、流程控制
1.if语句
if (i > 25) { alert("Greater than 25."); } else if (i < 0) { alert("Less than 0."); } else { alert("Between 0 and 25, inclusive."); }
2.do-while语句
var i = 0; do { i += 2; } while (i < 10); alert(i);
3.while语句
var i = 0; while (i < 10) { i += 2; }
4.for语句
var count = 10; for (var i = 0; i < count; i++){ alert(i); }
5.for in 语句
for-in 语句是一种精准的迭代语句,可以用来枚举对象的属性
for (var propName in window) { document.write(propName); }
使用 for-in 循环来显示了 BOM 中 window 对象的所有属性
6.label语句
使用 label 语句可以在代码中添加标签,当break label 或 continue label 会跳到标签位置
start: for (var i=0; i < count; i++) { alert(i); }
这个例子中定义的 start 标签可以在将来由 break 或 continue 语句引用。加标签的语句一般都
要与 for 语句等循环语句配合使用
7.break和continue语句
break跳出本层循环
var num = 0; for (var i=1; i < 10; i++) { if (i % 5 == 0) { break; } num++; } alert(num); //4
continue跳出本次循环
var num = 0; for (var i=1; i < 10; i++) { if (i % 5 == 0) { continue; } num++; } alert(num); //8
配合label使用
var num = 0; outermost: for (var i=0; i < 10; i++) { for (var j=0; j < 10; j++) { if (i == 5 && j == 5) { break outermost; } num++; } } alert(num); //55
建议如果使用 label 语句,一定要使用描述性的标签,同时不要嵌套过多的循环
8.with语句
不建议使用
9.switch语句
switch (i) { case 25: alert("25"); break; case 35: alert("35"); break; case 45: alert("45"); break; default: alert("Other"); }
合并两种情形
switch (i) { case 25: /* 合并两种情形 */ case 35: alert("25 or 35"); break; case 45: alert("45"); break; default: alert("Other"); }
var num = 25; switch (true) { case num < 0: alert("Less than 0."); break; case num >= 0 && num <= 10: alert("Between 0 and 10."); break; case num > 10 && num <= 20: alert("Between 10 and 20."); break; default: alert("More than 20."); }
三、函数
定义函数
function sayHi(name, message) { alert("Hello " + name + "," + message); }
调用函数
sayHi("Nicholas", "how are you today?");
ECMAScript 中的函数在定义时不必指定是否返回值
理解参数
ECMAScript 函数的参数与大多数其他语言中函数的参数有所不同。 ECMAScript 函数不介意传递进
来多少个参数,也不在乎传进来参数是什么数据类型。也就是说,即便你定义的函数只接收两个参数,
在调用这个函数时也未必一定要传递两个参数。可以传递一个、三个甚至不传递参数,而解析器永远不
会有什么怨言。之所以会这样,原因是 ECMAScript 中的参数在内部是用一个数组来表示的。函数接收
到的始终都是这个数组,而不关心数组中包含哪些参数(如果有参数的话)。如果这个数组中不包含任
何元素,无所谓;如果包含多个元素,也没有问题。实际上,在函数体内可以通过 arguments 对象来
访问这个参数数组,从而获取传递给函数的每一个参数。
其实, arguments 对象只是与数组类似(它并不是 Array 的实例),因为可以使用方括号语法访
问它的每一个元素(即第一个元素是 arguments[0],第二个元素是 argumetns[1],以此类推),使
用 length 属性来确定传递进来多少个参数。在前面的例子中, sayHi()函数的第一个参数的名字叫
name,而该参数的值也可以通过访问 arguments[0]来获取。因此,那个函数也可以像下面这样重写,
即不显式地使用命名参数:
function sayHi() { alert("Hello " + arguments[0] + "," + arguments[1]); } FunctionExample05.
这个重写后的函数中不包含命名的参数。虽然没有使用 name 和 message 标识符,但函数的功能
依旧。这个事实说明了 ECMAScript 函数的一个重要特点:命名的参数只提供便利,但不是必需的。另
外,在命名参数方面,其他语言可能需要事先创建一个函数签名,而将来的调用必须与该签名一致。但
在 ECMAScript 中,没有这些条条框框,解析器不会验证命名参数。
通过访问 arguments 对象的 length 属性可以获知有多少个参数传递给了函数。下面这个函数会
在每次被调用时,输出传入其中的参数个数:
function howManyArgs() { alert(arguments.length); } howManyArgs("string", 45); //2 howManyArgs(); //0 howManyArgs(12); //1
function doAdd() { if(arguments.length == 1) { alert(arguments[0] + 10); } else if (arguments.length == 2) { alert(arguments[0] + arguments[1]); } } doAdd(10); //20 doAdd(30, 20); //50
没有重载
如果在 ECMAScript 中定义了两个名字相同的函数,则该名字只属于后定义的函数
function addSomeNumber(num){ return num + 100; } function addSomeNumber(num) { return num + 200; } var result = addSomeNumber(100); //300