九、alert / prompt / confirm
1. alert
显示一个警告对话框,上面显示有指定的文本内容以及一个“确定”按钮。 注意:弹出模态框,并暂停脚本,直到用户点击“确定”按钮。
// 语法 window.alert(message); alert(message); // 示例 alert('hello leo!');
message
是要显示在对话框中的文本字符串,如果传入其他类型的值,会转换成字符串。
2. prompt
显示一个对话框,对话框中包含一条文字信息,用来提示用户输入文字。 注意:弹出模态框,并暂停脚本,直到用户点击“确定”按钮。 当点击确定返回文本,点击取消或按下 Esc 键返回 null
。 语法如下:
let result = window.prompt(text, value);
result
用来存储用户输入文字的字符串,或者是 null。text
用来提示用户输入文字的字符串,如果没有任何提示内容,该参数可以省略不写。value
文本输入框中的默认值,该参数也可以省略不写。不过在 Internet Explorer 7 和 8 中,省略该参数会导致输入框中显示默认值"undefined"。
3. confirm
Window.confirm()
方法显示一个具有一个可选消息和两个按钮(确定和取消)的模态对话框。 注意:弹出模态框,并暂停脚本,直到用户点击“确定”按钮。 语法如下:
let result = window.confirm(message);
- message 是要在对话框中显示的可选字符串。
- result 是一个布尔值,表示是选择确定还是取消 (true表示OK)。
十、条件运算符:if 和 '?'
1. if 语句
当 if 语句当条件表达式,会将表达式转换为布尔值,当为 truthy 时执行里面代码。 转换规则如:
- 数字
0
、空字符串""
、null
、undefined
和NaN
都会被转换成false
。因为他们被称为 “falsy” 值。 - 其他值被转换为
true
,所以它们被称为 “truthy”。
2. 三元运算符
条件(三元)运算符是 JavaScript 仅有的使用三个操作数的运算符。一个条件后面会跟一个问号(?),如果条件为 truthy ,则问号后面的表达式A将会执行;表达式A后面跟着一个冒号(:),如果条件为 falsy ,则冒号后面的表达式B将会执行。本运算符经常作为 [if](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/if...else)
语句的简捷形式来使用。 语法:
condition ? exprIfTrue : exprIfFalse
- condition 计算结果用作条件的表达式。
- exprIfTrue 如果表达式 condition 的计算结果是 truthy(它和 true 相等或者可以转换成 true ),那么表达式 exprIfTrue 将会被求值。
- exprIfFalse 如果表达式 condition 的计算结果是 falsy(它可以转换成 false ),那么表达式 exprIfFalse 将会被执行。 示例:
let getUser = function(name){ return name === 'leo' ? 'hello leo!' : 'unknow user'; } // 可以简写如下: let getUser = name => name === 'leo' ? 'hello leo!' : 'unknow user'; getUser('leo'); // "hello leo!" getUser('pingan'); // "unknow user"
十一、逻辑运算符
详细可以阅读《MDN 逻辑运算符》 。
1. 运算符介绍
逻辑运算符如下表所示 (其中_expr_
可能是任何一种类型, 不一定是布尔值):
运算符 | 语法 | 说明 |
逻辑与,AND(&& ) |
_expr1_ && _expr2_ |
若 expr**1** 可转换为 true ,则返回 expr**2** ;否则,返回 expr**1** 。 |
逻辑或,OR(|| ) |
_expr1_ || _expr2_ |
若 expr**1** 可转换为 true ,则返回 expr**1** ;否则,返回 expr**2** 。 |
逻辑非,NOT(! ) |
!_expr_ |
若 expr 可转换为 true ,则返回 false ;否则,返回 true 。 |
如果一个值可以被转换为 true
,那么这个值就是所谓的 truthy,如果可以被转换为 false
,那么这个值就是所谓的 falsy。 会被转换为 false
的表达式有:
null
;NaN
;0
;- 空字符串(
""
or''
or ````); undefined
。 尽管&&
和||
运算符能够使用非布尔值的操作数, 但它们依然可以被看作是布尔操作符,因为它们的返回值总是能够被转换为布尔值。如果要显式地将它们的返回值(或者表达式)转换为布尔值,请使用双重非运算符(即!!
)或者Boolean构造函数。 JavaScript 里有三个逻辑运算符:||
(或),&&
(与),!
(非)。
2. 运算符示例
- 逻辑与(&&) 所有条件都为 true 才返回 true,否则为 false。
a1 = true && true // t && t 返回 true a2 = true && false // t && f 返回 false a3 = false && true // f && t 返回 false a4 = false && (3 == 4) // f && f 返回 false a5 = "Cat" && "Dog" // t && t 返回 "Dog" a6 = false && "Cat" // f && t 返回 false a7 = "Cat" && false // t && f 返回 false a8 = '' && false // f && f 返回 "" a9 = false && '' // f && f 返回 false
- 逻辑或( || ) 所有条件有一个为 true 则返回 true,否则为 false。
o1 = true || true // t || t 返回 true o2 = false || true // f || t 返回 true o3 = true || false // t || f 返回 true o4 = false || (3 == 4) // f || f 返回 false o5 = "Cat" || "Dog" // t || t 返回 "Cat" o6 = false || "Cat" // f || t 返回 "Cat" o7 = "Cat" || false // t || f 返回 "Cat" o8 = '' || false // f || f 返回 false o9 = false || '' // f || f 返回 ""
- 逻辑非( ! )
n1 = !true // !t 返回 false n2 = !false // !f 返回 true n3 = !'' // !f 返回 true n4 = !'Cat' // !t 返回 false
- 双重非运( !! )
n1 = !!true // !!truthy 返回 true n2 = !!{} // !!truthy 返回 true: 任何 对象都是 truthy 的… n3 = !!(new Boolean(false)) // …甚至 .valueOf() 返回 false 的布尔值对象也是! n4 = !!false // !!falsy 返回 false n5 = !!"" // !!falsy 返回 false n6 = !!Boolean(false) // !!falsy 返回 false
3. 布尔值转换规则
- 将 && 转换为 ||
condi1 && confi2 // 转换为 !(!condi1 || !condi2)
- 将 || 转换为 &&
condi1 || condi2 // 转换为 !(!condi1 && !condi2)
4. 短路取值
由于逻辑表达式的运算顺序是从左到右,也可以用以下规则进行"短路"计算:
(some falsy expression) && (_expr)_
短路计算的结果为假。(some truthy expression) || _(expr)_
短路计算的结果为真。 短路意味着上述表达式中的expr部分不会被执行,因此expr的任何副作用都不会生效(举个例子,如果expr是一次函数调用,这次调用就不会发生)。造成这种现象的原因是,整个表达式的值在第一个操作数被计算后已经确定了。看一个例子:
function A(){ console.log('called A'); return false; } function B(){ console.log('called B'); return true; } console.log( A() && B() ); // logs "called A" due to the function call, // then logs false (which is the resulting value of the operator) console.log( B() || A() ); // logs "called B" due to the function call, // then logs true (which is the resulting value of the operator)
5. 注意
与运算 &&
的优先级比或运算 ||
要高。 所以代码 a && b || c && d
完全跟 &&
表达式加了括号一样:(a && b) || (c && d)
。
十二、循环:while 和 for
1. while 循环
详细可以阅读《MDN while》 。 while 语句可以在某个条件表达式为真的前提下,循环执行指定的一段代码,直到那个表达式不为真时结束循环。 如:
var n = 0; var x = 0; while (n < 3) { n++; x += n; }
当循环体为单行时,可以不写大括号:
let i = 3; while(i) console.log(i --);
2. do...while 循环
详细可以阅读《MDN do...while》 。 do...while
语句创建一个执行指定语句的循环,直到condition
值为 false。在执行statement
后检测condition
,所以指定的statement
至少执行一次。 如:
var result = ''; var i = 0; do { i += 1; result += i + ' '; } while (i < 5);
3. for 循环
详细可以阅读《MDN for》 。 for
语句用于创建一个循环,它包含了三个可选的表达式,这三个表达式被包围在圆括号之中,使用分号分隔,后跟一个用于在循环中执行的语句(通常是一个块语句)。 语法如:
for (begin; condition; step) { // ……循环体…… }
示例:
for (let i = 0; i < 3; i++) { console.log(i); }
描述:
begin | i = 0 |
进入循环时执行一次。 |
condition | i < 3 |
在每次循环迭代之前检查,如果为 false,停止循环。 |
body(循环体) | alert(i) |
条件为真时,重复运行。 |
step | i++ |
在每次循环体迭代后执行。 |
4. 可选的 for 表达式
for
语句头部圆括号中的所有三个表达式都是可选的。
- 不指定表达式中初始化块
var i = 0; for (; i < 3; i++) { console.log(i); }
- 不指定表达式中条件块,这就必须要求在循环体中结束循环,否则会出现死循环
for (var i = 0;; i++) { console.log(i); if (i > 3) break; }
- 不指定所有表达式,也需要在循环体中指定结束循环的条件
var i = 0; for (;;) { if (i > 3) break; console.log(i); i++; }
5. break 语句
详细可以阅读《MDN break》 。 break 语句中止当前循环,switch
语句或label
语句,并把程序控制流转到紧接着被中止语句后面的语句。 在 while 语句中:
function testBreak(x) { var i = 0; while (i < 6) { if (i == 3) { break; } i += 1; } return i * x; }
另外,也可以为代码块做标记,并在 break 中指定要跳过的代码块语句的 label:
outer_block:{ inner_block:{ console.log ('1'); break outer_block; // breaks out of both inner_block and outer_block console.log (':-('); // skipped } console.log ('2'); // skipped }
需要注意的是:break 语句需要内嵌在它所应用的标签或代码块中,否则报错:
block_1:{ console.log ('1'); break block_2; // SyntaxError: label not found } block_2:{ console.log ('2'); }
6. continue 语句
continue 声明终止当前循环或标记循环的当前迭代中的语句执行,并在下一次迭代时继续执行循环。 与 break
语句的区别在于, continue 并不会终止循环的迭代,而是:
i = 0; n = 0; while (i < 5) { i++; if (i === 3) { continue; } n += i; }
带 label:
var i = 0, j = 8; checkiandj: while (i < 4) { console.log("i: " + i); i += 1; checkj: while (j > 4) { console.log("j: "+ j); j -= 1; if ((j % 2) == 0) continue checkj; console.log(j + " is odd."); } console.log("i = " + i); console.log("j = " + j); }
7. 注意
禁止 break/continue
在 ‘?’ 的右边:
(i > 5) ? console.log(i) : continue; // continue 不允许在这个位置
这样会提示语法错误。 请注意非表达式的语法结构不能与三元运算符 ?
一起使用。特别是 break/continue
这样的指令是不允许这样使用的。
8. 总结
三种循环:
while
—— 每次迭代之前都要检查条件。do..while
—— 每次迭代后都要检查条件。for (;;)
—— 每次迭代之前都要检查条件,可以使用其他设置。 通常使用while(true)
来构造“无限”循环。这样的循环和其他循环一样,都可以通过break
指令来终止。 如果我们不想在当前迭代中做任何事,并且想要转移至下一次迭代,那么可以使用continue
指令。break/continue
支持循环前的标签。标签是break/continue
跳出嵌套循环以转到外部的唯一方法。
十三、"switch" 语句
switch
语句用来将表达式的值与 case 语句匹配,并执行与情况对应的语句。 switch
语句可以替代多个 if 判断,为多个分支选择的情况提供一个更具描述性的方式。
1. 语法
switch
语句至少包含一个 case
代码块和一个可选的 default
代码块:
switch(expression) { case 'value1': // do something ... [break] default: // ... [break] }
当 expression
表达式的值与 value1
匹配时,则执行其中代码块。 如果没有 case
子句匹配,则会选择 default
子句执行,若连 default
子句都没有,则直接执行到 switch
结束。
2. 使用 case 分组
所谓 case 分组,就是与多个 case 分支共享同一段代码,如下面例子中 case 1
和 case 2
:
let a = 2; switch (a) { case 1: // (*) 下面这两个 case 被分在一组 case 2: console.log('case is 1 or 2!'); break; case 3: console.log('case is 3!'); break; default: console.log('The result is default.'); } // 'case is 1 or 2!'
3. 注意点
expression
表达式的值与case
值的比较是严格相等:
function f(n){ let a ; switch(n){ case 1: a = 'number'; break; case '1': a = 'string'; break; default: a = 'default'; break; } console.log(a) } f(1); // number f('1'); // string
- **如果没有
break
,程序将不经过任何检查就会继续执行下一个 ****case**
:
let a = 2 + 2; switch (a) { case 3: console.log( 'Too small' ); case 4: console.log( 'Exactly!' ); case 5: console.log( 'Too big' ); default: console.log( "I don't know such values" ); } // Exactly! // Too big // I don't know such values
**default**
**放在 ****case**
之上不影响匹配:
function f(n){ switch (n) { case 2: console.log(2); break; default: console.log('default') break; case 1: console.log('1'); break; } } f(1); // 1 f(2); // 2 f(3); // default
switch
语句中存在let
/const
重复声明问题:
// 以下定义会报错 function f(n){ switch(n){ case 1: let msg = 'hello'; console.log(1); break; case 2: let msg = 'leo'; break; default: console.log('default'); break; } } // Error: Uncaught SyntaxError: Identifier 'msg' has already been declared
这是由于两个 let
处于同一个块级作用域,所以它们被认为是同一变量名的重复声明。 解决方式,只需要将 case
语句包装在括号内即可解决:
function f(n){ switch(n){ case 1:{ // added brackets let msg = 'hello'; console.log(msg); break; } case 2: { let msg = 'leo'; console.log(msg); break; } default: console.log('default'); break; } }
十四、函数
函数可以让一段代码被多次调用,避免重复代码。 如之前学习到的一些内置函数: alert(msg)
/ prompt(msg, default)
/ confirm(quesyion)
等。
1. 函数定义
定义函数有两种方式:函数声明和函数表达式。
1.1 函数声明
如定义一个简单 getUser
函数:
function getUser(name){ return 'hello ' + name; } getUser('leo"); // 函数调用
通过函数声明来定义函数时,需要由以下几部分组成:
- 函数名称 -
getUser
; - 函数参数列表 -
name
; - 函数的 JS 执行语句 -
return 'hello ' + name
。
1.2 函数表达式
类似声明变量,还是以 getUser
为例:
let getUser = function(name){ return 'hello ' + name; }
另外,函数表达式也可以提供函数名,并用于函数内部指代函数本身:
let fun = function f(n){ return n < 3 ? 1 : n * f(n - 1); } fun(3); // 3 fun(5); // 60
2. 函数调用
当定义一个函数后,它并不会自动执行,而是需要使用函数名称进行调用,如上面例子:
fun(3); // 3
只要注意: 使用 函数表达式 定义函数时,调用函数的方法必须写在定义之后,否则报错:
console.log(fun()); // Uncaught ReferenceError: fun is not defined let fun = function(){return 1};
而使用 函数声明 则不会出现该问题:
console.log(fun()); // 1 function fun(){return 1};
原因就是:函数提升仅适用于函数声明,而不适用于函数表达式。
3. 函数中的变量
在函数中,可以使用局部变量和外部变量。
3.1 局部变量
函数中声明的变量只能在该函数内可见。
let fun = function(){ let name = 'leo'; } fun(); console.log(name); // Uncaught ReferenceError: name is not defined
3.2 全局变量
函数内可以使用外部变量,并且可以修改外部变量的值。
let name = 'leo'; let fun = function(){ let text = 'Hello, ' + name; console.log(text); } fun(); // Hello, leo
当函数内也有与外部变量名称相同的变量,会忽略外部变量:
let name = 'leo'; let fun = function(){ let name = 'pingan8787'; let text = 'Hello, ' + name; console.log(text); } fun(); // Hello, pingan8787
4. 函数参数
从ECMAScript 6开始,有两个新的类型的参数:默认参数,剩余参数。
4.1 默认参数
若函数没有传入参数,则参数默认值为undefined
,通常设置参数默认值是这样做的:
// ES6 之前,没有设置默认值 function f(a, b){ b = b ? b : 1; return a * b; } f(2,3); // 6 f(2); // 2 // ES6,设置默认值 function f(a, b = 1){ return a * b; } f(2,3); // 6 f(2); // 2
4.2 剩余参数
可以将参数中不确定数量的参数表示成数组,如下:
function f (a, ...b){ console.log(a, b); } f(1,2,3,4); // a => 1 b => [2, 3, 4]
既然讲到参数,那就不能少了 arguments 对象。
4.3 arguments 对象
函数的实际参数会被保存在一个类似数组的arguments对象中。在函数内,我们可以使用 arguments 对象获取函数的所有参数:
let fun = function(){ console.log(arguments); console.log(arguments.length); } fun('leo'); // Arguments ["leo", callee: ƒ, Symbol(Symbol.iterator): ƒ] // 1
以一个实际示例介绍,实现将任意数量参数连接成一个字符串,并输出的函数:
let argumentConcat = function(separator){ let result = '', i; for(i = 1; i < arguments.length; i ++){ result += arguments[i] + separator; } return result; } argumentConcat(',', 'leo', 'pingan'); //"leo,pingan,"
5. 函数返回值
在函数任意位置,指定 return
指令来停止函数的执行,并返回函数指定的返回值。
let sum = function(a, b){ return a + b; }; let res = sum(1, 2); console.log(res); // 3
默认空值的 return
或没有 return
的函数返回值为 undefined
。
十五、函数表达式
函数表达式是一种函数定义方式,在前面章节中已经介绍到了,这个部分将着重介绍 函数表达式 和 函数声明 的区别:
1. 语法差异
// 函数表达式 let fun = function(){}; // 函数声明 function fun(){}
2. 创建时机差异
函数表达式会在代码执行到达时被创建,并且仅从那一刻可用。 而函数声明被定义之前,它就可以被调用。
// 函数表达式 fun(); // Uncaught ReferenceError: fun is not defined let fun = function(){console.log('leo')}; // 函数声明 fun(); // "leo" function fun(){console.log('leo')};
3. 使用建议
建议优先考虑函数声明语法,它能够为组织代码提供更多灵活性,因为我们可以在声明函数前调用该函数。
十六、箭头函数
本章节简单介绍箭头函数基础知识,后面章节会完整介绍。函数箭头表达式是ES6新增的函数表达式的语法,也叫胖箭头函数,变化:更简洁的函数和this
。
1. 代码更简洁
// 有1个参数 let f = v => v; // 等同于 let f = function (v){return v}; // 有多个参数 let f = (v, i) => {return v + i}; // 等同于 let f = function (v, i){return v + i}; // 没参数 let f = () => 1; // 等同于 let f = function (){return 1}; let arr = [1,2,3,4]; arr.map(ele => ele + 1); // [2, 3, 4, 5]
2. 注意点
- 箭头函数不存在
this
; - 箭头函数不能当做构造函数,即不能用
new
实例化; - 箭头函数不存在
arguments
对象,即不能使用,可以使用rest
参数代替; - 箭头函数不能使用
yield
命令,即不能用作Generator函数。 一个简单的例子:
function Person(){ this.age = 0; setInterval(() => { this.age++; }, 1000); } var p = new Person(); // 定时器一直在执行 p的值一直变化