流程控制
JavaScript 流程控制资料索引
循环
for
forEach
for-in
for-of
do-while
流程控制
运算符
基本运算符
加法运算符
在
- 运算子之中存在字符串
两个运算子之中,只要有一个是字符串,则另一个不管是什么类型,都会被自动转为字符串,然后执行字符串连接运算。前面的《自动转换为字符串》一节,已经举了很多例子。
- 两个运算子都为数值或布尔值
这种情况下,执行加法运算,布尔值转为数值
true + 5 // 6
true + true // 2
- 运算子之中存在对象
运算子之中存在对象
1 + [1,2]
// "11,2"
上面代码的运行顺序是,先调用
1 + {a:1}
// "1[object Object]"
对象
有趣的是,如果更换上面代码的运算次序,就会得到不同的值。
{a:1} + 1
// 1
原来此时,
({a:1})+1
"[object Object]1"
将
1 + {valueOf:function(){return 2;}}
// 3
上面代码的
1 + {valueOf:function(){return {};}}
// "1[object Object]"
上面代码的
1 + {valueOf:function(){return {};}, toString:function(){return 2;}}
// 3
上面代码的
1 + {valueOf:function(){return {};}, toString:function(){return {};}}
// TypeError: Cannot convert object to primitive value
上面代码的
[] + []
// ""
首先,对空数组调用
[] + {}
// "[object Object]"
这等同于空字符串与字符串 “[object Object]” 相加。因此,结果就是 “[object Object]”。
{} + []
// 0
+ []
// Number([])
// Number([].toString())
// Number("")
// 0
如果
({}) + []
// "[object Object]"
{} + {}
// NaN
+ {}
// Number({})
// Number({}.toString())
// Number("[object Object]")
逻辑运算符
等于运算符
==,两边值类型不同的时候,要先进行类型转换,再比较。
===,不做类型转换,类型不同的一定不等。即严格等于。
举例而言:
"1" == true
类型不等,
const a = 3;
const b = "3";
a==b 返回 true
a===b 返回 false
条件
if
if (expression)
statement
if (m === 3) {
// then
} else {
// else
}
switch
多个
switch (fruit) {
case "banana":
// ...
break;
case "apple":
// ...
break;
default:
// ...
}
上面代码根据变量
循环
for
最原始的方式即是用
for (const index = 0; index < myArray.length; index++) {
console.log(myArray[index]);
}
forEach: 数组原型方法
自
myArray.forEach(function (value) {
console.log(value);
});
//或者带下标的方式遍历
myArray.forEach(function (value, index) {
console.log(value);
});
这段代码看起来更加简洁,但这种方法也有一个小缺陷:你不能使用
for-in: 适用于对于对象的遍历( 会遍历对象的原型)
for-in
循环可以用于数组与对象的遍历,但是不建议用于数组的遍历中,如下是个简单的示例:
for (const index in myArray) {
// 千万别这样做
console.log(myArray[index]);
}
在这段代码中,赋给
for-of: 适用于对于数组以及集合类型的遍历
//遍历数组,直接返回值
for (const value of myArray) {
console.log(value);
}
//遍历map,返回的是一个Array;也可以直接调用解构。
//注意,for-of不支持普通的Object
for (const data of myMap) {
console.log(data); //Array
}
for (const chr of "") {
alert(chr);
}
它同样支持
// 基于单词数组创建一个set对象
const uniqueWords = new Set(words);
生成
for (const word of uniqueWords) {
console.log(word);
}
for (const [key, value] of phoneBookMap) {
console.log(key + "'s phone number is: " + value);
}
现在,你只需记住:未来的
// 向控制台输出对象的可枚举属性
for (const key of Object.keys(someObject)) {
console.log(key + ": " + someObject[key]);
}
while
while (expression)
statement
while (expression){
statement
}
do…while 循环
do…
do
statement
while(expression);
// 或者
do {
statement
} while(expression);
不管条件是否为真,
控制
break & continue
const i = 0;
while (i < 100) {
console.log("i当前为:" + i);
i++;
if (i === 10) break;
}
上面代码只会执行
const i = 0;
while (i < 100) {
i++;
if (i % 2 === 0) continue;
console.log("i当前为:" + i);
}
上面代码只有在
如果存在多重循环,不带参数的
标签
top: for (const i = 0; i < 3; i++) {
for (const j = 0; j < 3; j++) {
if (i === 1 && j === 1) break top;
console.log("i=" + i + ",j=" + j);
}
}
// i=0,j=0
// i=0,j=1
// i=0,j=2
// i=1,j=0
上面代码为一个双重循环区块,加上了
top: for (const i = 0; i < 3; i++) {
for (const j = 0; j < 3; j++) {
if (i === 1 && j === 1) continue top;
console.log("i=" + i + ",j=" + j);
}
}
// i=0,j=0
// i=0,j=1
// i=0,j=2
// i=1,j=0
// i=2,j=0
// i=2,j=1
// i=2,j=2
上面代码在满足一定条件时,使用
迭代器
生成器是
function* ticketGenerator(){}
function* ticketGenerator(){
yield 1;
yield 2;
yield 3;
}
当你调用一个
const takeANumber = ticketGenerator();
takeANumber.next();
//>{value: 1, done: false}
takeANumber.next();
//>{value: 2, done: false}
takeANumber.next();
//>{value: 3, done: false}
takeANumber.next();
//>{value: undefined, done: true}
值得特别一提的是,生成器不是线程,在支持线程的语言中,多段代码可以同时运行,通通常导致竞态条件和非确定性,不过同时也带来不错的性能。生成器则完全不同。当生成器运行时,它和调用者处于同一线程中,拥有确定的连续执行顺序,永不并发。与系统线程不同的是,生成器只有在其函数体内标记为
function* fab(max) {
const count = 0,
last = 0,
current = 1;
while (max > count++) {
yield current;
const tmp = current;
current += last;
last = tmp;
}
}
const o = fab(10),
ret,
result = [];
while (!(ret = o.next()).done) {
result.push(ret.value);
}
console.log(result); // [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
Modules( 模块)
Export
export function generateRandom() {
return Math.random();
}
export function sum(a, b) {
return a + b;
}
//或者在底部使用
export { generateRandom, sum };
在
import { generateRandom, sum as s } from "utility"; //这里默认使用了解构赋值
console.log(generateRandom()); //logs a random number
console.log(s(1, 2)); //3
如果只需要引入某个单独的模块,可以利用:
import { sum } from "utility";
同时,也可以将整个模块引入当做一个
import 'utility' as utils;
console.log(utils.generateRandom()); //logs a random number
console.log(utils.sum(1, 2)); //3
Default Exports and Re-exporting
utility.js
const utils = {
generateRandom: function () {
return Math.random();
},
sum: function (a, b) {
return a + b;
},
};
export default utils;
app.js
import utils from "utility"; //这里需要注意下,如果export时不是用的{},那么这里也就不用添加{}
console.log(utils.generateRandom()); //logs a random number
console.log(utils.sum(1, 2)); //3
export default utils; //exports the imported value
总结而言,全部的
import name from "module-name";
import * as name from "module-name";
import { member } from "module-name";
import { member as alias } from "module-name";
import { member1, member2 } from "module-name";
import { member1, member2 as alias2, [...] } from "module-name";
import defaultMember, { member [, [...] ] } from "module-name";
import defaultMember, * as alias from "module-name";
import defaultMember from "module-name";
import "module-name";
Loaders
异常处理
Error( 异常定义与类型)
Error 对象
一旦代码解析或运行时发生错误,
- name:错误名称
- message:错误提示信息
- stack:错误的堆栈
( 非标准属性,但是大多数平台支持)
一般来说,利用
if (error.name) {
console.log(error.name + ": " + error.message);
}
上面代码表示,显示错误的名称以及出错提示信息。
function throwit() {
throw new Error("");
}
function catchit() {
try {
throwit();
} catch (e) {
console.log(e.stack); // print stack trace
}
}
catchit();
// Error
// at throwit (~/examples/throwcatch.js:9:11)
// at catchit (~/examples/throwcatch.js:3:9)
// at repl:1:5
错误类型
- SyntaxError
SyntaxError是解析代码时发生的语法错误。
// 变量名错误
const 1a;
// 缺少括号
console.log 'hello');
- ReferenceError
unknownVariable;
// ReferenceError: unknownVariable is not defined
另一种触发场景是,将一个值分配给无法分配的对象,比如对函数的运行结果或者
console.log() = 1;
// ReferenceError: Invalid left-hand side in assignment
this = 1;
// ReferenceError: Invalid left-hand side in assignment
上面代码对函数
- RangeError
new Array(-1)(
// RangeError: Invalid array length
1234
).toExponential(21);
// RangeError: toExponential() argument must be between 0 and 20
- TypeError
new 123();
//TypeError: number is not a func
const obj = {};
obj.unknownMethod();
// TypeError: undefined is not a function
上面代码的第二种情况,调用对象不存在的方法,会抛出
- URIError
decodeURI("%2");
// URIError: URI malformed
- EvalError
以上这
new Error("出错了!");
new RangeError("出错了,变量超出有效范围!");
new TypeError("出错了,变量类型无效!");
上面代码表示新建错误对象的实例,实质就是手动抛出错误。可以看到,错误对象的构造函数接受一个参数,代表错误提示信息
自定义错误(ES6 Based)
class ExtendableError extends Error {
constructor(message) {
super(message);
this.name = this.constructor.name;
this.message = message;
Error.captureStackTrace(this, this.constructor.name);
}
}
class MyError extends ExtendableError {
constructor(m) {
super(m);
}
}
const myerror = new MyError("ll");
console.log(myerror.message);
console.log(myerror instanceof Error);
console.log(myerror.name);
console.log(myerror.stack);
Throw
throw "Error!";
throw 42;
throw true;
throw {
toString: function () {
return "Error!";
},
};
上面代码表示,
throw new Error("出错了!");
Try-Catch-Finally
try {
throw new Error("出错了!");
} catch (e) {
console.log(e.name + ": " + e.message); // Error: 出错了!
console.log(e.stack); // 不是标准属性,但是浏览器支持
}
// Error: 出错了!
// Error: 出错了!
// at <anonymous>:3:9
// at Object.InjectedScript._evaluateOn (<anonymous>:895:140)
// at Object.InjectedScript._evaluateAndWrap (<anonymous>:828:34)
// at Object.InjectedScript.evaluate (<anonymous>:694:21)
上面代码中,
function throwIt(exception) {
try {
throw exception;
} catch (e) {
console.log("Caught: " + e);
}
}
throwIt(3);
// Caught: 3
throwIt("hello");
// Caught: hello
throwIt(new Error("An error happened"));
// Caught: Error: An error happened
为了捕捉不同类型的错误,
try {
foo.bar();
} catch (e) {
if (e instanceof EvalError) {
console.log(e.name + ": " + e.message);
} else if (e instanceof RangeError) {
console.log(e.name + ": " + e.message);
}
// ...
}
同时为了更好地执行逻辑顺序,在
openFile();
try {
writeFile(Data);
} catch (e) {
handleError(e);
} finally {
closeFile();
}