aboutsummaryrefslogtreecommitdiff
path: root/files/zh-cn/web/javascript/guide/control_flow_and_error_handling/index.html
blob: cc837aafb032a72d00c0e44cf69ab6ded99895a6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
---
title: 流程控制与错误处理
slug: Web/JavaScript/Guide/Control_flow_and_error_handling
tags:
  - JavaScript
translation_of: Web/JavaScript/Guide/Control_flow_and_error_handling
---
<p>{{jsSidebar("JavaScript Guide")}}{{PreviousNext("Web/JavaScript/Guide/Grammar_and_Types", "Web/JavaScript/Guide/Loops_and_iteration")}}</p>

<p>JavaScript 提供一套灵活的语句集,特别是控制流语句,你可以用它在你的应用程序中实现大量的交互性功能。本章节我们将带来关于JavaScript语句的一些概览。</p>

<p> 这一章中的语句,在 <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements">JavaScript参考</a> 中包含更为详尽的细节。在 JavaScript 代码中,分号(;)字符被用来分割语句。</p>

<p>在JavaScript中,任何表达式(expression)都可以看作一条语句(statement),如果你想了解表达式的详细信息,可以阅读<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators">表达式与运算符(Expressions and operators)</a>这一章节。</p>

<h2 id="语句块">语句块</h2>

<p>最基本的语句是用于组合语句的语句块。该块由一对大括号界定:</p>

<pre class="syntaxbox">{
   statement_1;
   statement_2;
   statement_3;
   .
   .
   .
   statement_n;
}</pre>

<h3 id="示例"><strong>示例</strong></h3>

<p>语句块通常用于流程控制,如<code>if</code><code>for</code><code>while</code>等等。</p>

<pre class="brush: js">while (x &lt; 10) {
  x++;
}</pre>

<p>这里<code>{ x++; }</code>就是语句块。</p>

<div>
<p><strong>重要</strong>:在ECMAScript 6标准之前,Javascript没有块作用域。在一个块中引入的变量的作用域是包含函数或脚本,并且设置它们的效果会延续到块之外。换句话说,块语句不定义范围。JavaScript中的“独立”块会产生与C或Java中完全不同的结果。示例:</p>
</div>

<pre class="brush: js">var x = 1;
{
  var x = 2;
}
alert(x); // 输出的结果为 2
</pre>

<p>这段代码的输出是<strong>2</strong>,这是因为块级作用域中的 var x变量声明与之前的声明在同一个作用域内。在C语言或是Java语言中,同样的代码输出的结果是1。</p>

<p>从 ECMAScript 2015 开始,使用 <code>let</code> 和<code>const</code>变量是块作用域的。 更多信息请参考 {{jsxref("Statements/let", "let")}}{{jsxref("Statements/const", "const")}}</p>

<h2 id="条件判断语句">条件判断语句</h2>

<p>条件判断语句指的是根据指定的条件所返回的结果(真或假或其它预定义的),来执行特定的语句。JavaScript 支持两种条件判断语句:<code>if...else</code><code>switch</code></p>

<h3 id="if...else_语句"><code>if...else</code> 语句</h3>

<p>当一个逻辑条件为真,用if语句执行一个语句。当这个条件为假,使用可选择的 else 从句来执行这个语句。if 语句如下所示:</p>

<pre class="brush: js">if (condition) {
  statement_1;
}else {
  statement_2;
} //推荐使用严格的语句块模式,语句else可选
</pre>

<p>条件可以是任何返回结果被计算为true 或 false的表达式。如果条件表达式返回的是 true,statement_1 语句会被执行;否则,statement_2 被执行。statement_1 和 statement_2 可以是任何语句,甚至你可以将另一个if语句嵌套其中。 </p>

<p>你也可以组合语句通过使用 <code>else if</code> 来测试连续多种条件判断,就像下面一样:</p>

<pre class="brush: js">if (condition_1) {
  statement_1;
}else if (condition_2) {
  statement_2;
}else if (condition_n_1) {
  statement_n;
}else {
  statement_last;
}
</pre>

<p>要执行多个语句,可以使用语句块({ ... }) 来分组这些语句。通常,总是使用语句块是一个好的习惯,特别是在代码涉及比较多的 if 语句时:</p>

<pre class="brush: js">if (条件) {
  当条件为真的时候,执行语句1;
  当条件为真的时候,执行语句2;
} else {
  当条件为假的时候,执行语句3;
  当条件为假的时候,执行语句4;
}
</pre>

<p>不建议在条件表达式中使用赋值语句,因为在快速查阅代码时容易把它看成等值比较。例如,不要使用下面的代码:</p>

<div class="warning">
<p><strong>警告:</strong></p>
<pre class="brush: js">
  if(x = y){
    /*  语句  */
  }
</pre>
</div>

<p>如果你需要在条件表达式中使用赋值,通常在赋值语句前后额外添加一对括号。例如: </p>



<pre><code>if ((x = y)) {
  /* statements here */
}</code></pre>



<h4 id="错误的值">错误的值</h4>

<p>下面这些值将被计算出 false (also known as {{Glossary("Falsy")}} values):</p>

<ul>
 <li><code>false</code></li>
 <li><code>undefined</code></li>
 <li><code>null</code></li>
 <li><code>0</code></li>
 <li><code>NaN</code></li>
 <li>空字符串(<code>""</code></li>
</ul>

<p>当传递给条件语句所有其他的值,包括所有对象会被计算为真 。</p>

<p>请不要混淆原始的布尔值<code>true</code><code>false</code>{{jsxref("Boolean")}}对象的真和假。例如:</p>

<pre class="brush: js">var b = new Boolean(false);
if (b) //结果视为真
if (b == true) // 结果视为假</pre>

<h4 id="示例_2"><strong>示例</strong></h4>

<p>在以下示例中,如果<code>Text</code>对象中的字符数为3,函数<code>checkData</code>将返回<code>true</code>;否则,显示警报并返回<code>false</code></p>

<pre class="brush: js">function checkData() {
  if (document.form1.threeChar.value.length == 3) {
    return true;
  } else {
    alert("Enter exactly three characters. " +
      document.form1.threeChar.value + " is not valid.");
    return false;
  }
}
</pre>

<h3 id="switch_语句"><code>switch</code> 语句</h3>

<p><code>switch</code> 语句允许一个程序求一个表达式的值并且尝试去匹配表达式的值到一个 <code>case</code> 标签。如果匹配成功,这个程序执行相关的语句。<code>switch</code> 语句如下所示:</p>

<pre class="brush: js">switch (expression) {
   case label_1:
      statements_1
      [break;]
   case label_2:
      statements_2
      [break;]
   ...
   default:
      statements_def
      [break;]
}
</pre>

<p>程序首先查找一个与 <code>expression </code>匹配的 <code>case </code>语句,然后将控制权转移到该子句,执行相关的语句。如果没有匹配值, 程序会去找 <code>default </code>语句,如果找到了,控制权转移到该子句,执行相关的语句。如果没有找到 <code>default</code>,程序会继续执行 <code>switch </code>语句后面的语句。<code>default</code> 语句通常出现在switch语句里的最后面,当然这不是必须的。</p>

<p><code>可选的 break</code> 语句与每个 <code>case</code> 语句相关联, 保证在匹配的语句被执行后程序可以跳出 <code>switch </code>并且继续执行 <code>switch</code> 后面的语句。如果break被忽略,则程序将继续执行switch语句中的下一条语句。</p>

<p><strong>示例</strong><br>
 在如下示例中, 如果 <code>fruittype</code> 等于 "Bananas", 程序匹配到对应 "Bananas" 的<code>case</code> 语句,并执行相关语句。 当执行到 <code>break</code> 时,程序结束了 <code>switch</code> 并执行 <code>switch</code> 后面的语句。 如果不写 <code>break</code> ,那么程序将会执行 <code>case "Cherries"</code> 下的语句。</p>

<pre class="brush: js">switch (fruittype) {
   case "Oranges":
      document.write("Oranges are $0.59 a pound.&lt;br&gt;");
      break;
   case "Apples":
      document.write("Apples are $0.32 a pound.&lt;br&gt;");
      break;
   case "Bananas":
      document.write("Bananas are $0.48 a pound.&lt;br&gt;");
      break;
   case "Cherries":
      document.write("Cherries are $3.00 a pound.&lt;br&gt;");
      break;
   case "Mangoes":
   case "Papayas":
      document.write("Mangoes and papayas are $2.79 a pound.&lt;br&gt;");
      break;
   default:
      document.write("Sorry, we are out of " + fruittype + ".&lt;br&gt;");
}
document.write("Is there anything else you'd like?&lt;br&gt;");
</pre>

<h2 id="异常处理语句">异常处理语句</h2>

<p>你可以用 <code>throw</code> 语句抛出一个异常并且用 <code>try...catch</code> 语句捕获处理它。</p>

<ul>
 <li><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Control_flow_and_error_handling$edit#throw_statement"><code>throw</code> </a>语句</li>
 <li><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Control_flow_and_error_handling$edit#try...catch_statement"><code>try...catch</code> </a>语句</li>
</ul>

<h3 id="异常类型">异常类型</h3>

<p>JavaScript 可以抛出任意对象。然而,不是所有对象能产生相同的结果。尽管抛出数值或者字母串作为错误信息十分常见,但是通常用下列其中一种异常类型来创建目标更为高效:</p>

<ul>
 <li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error#Error_types">ECMAScript exceptions</a></li>
 <li>{{domxref("DOMException")}} and {{domxref("DOMError")}}</li>
</ul>

<h3 id="throw_语句"><code>throw</code> 语句</h3>

<p>使用<code>throw</code>语句抛出一个异常。当你抛出异常,你规定一个含有值的表达式要被抛出。</p>

<pre class="brush: js">throw expression;
</pre>

<p>你可以抛出任意表达式而不是特定一种类型的表达式。下面的代码抛出了几个不同类型的表达式:</p>

<pre class="brush: js">throw "Error2";   // String type
throw 42;         // Number type
throw true;       // Boolean type
throw {toString: function() { return "I'm an object!"; } };

</pre>

<div class="note">
  <p><strong>备注:</strong>你可以在抛出异常时声明一个对象。那你就可以在catch块中查询到对象的属性。</p>
</div>

<pre class="brush: js">// Create an object type UserException
function UserException (message){
  this.message=message;
  this.name="UserException";
}

// Make the exception convert to a pretty string when used as
// a string (e.g. by the error console)
UserException.prototype.toString = function (){
  return this.name + ': "' + this.message + '"';
}

// Create an instance of the object type and throw it
throw new UserException("Value too high");</pre>

<h3 id="try...catch_语句"><code>try...catch</code> 语句</h3>

<p><code>try...catch</code> 语句标记一块待尝试的语句,并规定一个以上的响应应该有一个异常被抛出。如果我们抛出一个异常,<code>try...catch</code>语句就捕获它。</p>

<p><code>try...catch</code> 语句有一个包含一条或者多条语句的try代码块,0个或1个的<code>catch</code>代码块,catch代码块中的语句会在try代码块中抛出异常时执行。 换句话说,如果你在try代码块中的代码如果没有执行成功,那么你希望将执行流程转入catch代码块。如果try代码块中的语句(或者<code>try</code> 代码块中调用的方法)一旦抛出了异常,那么执行流程会立即进入<code>catch</code> 代码块。如果try代码块没有抛出异常,catch代码块就会被跳过。<code>finally</code> 代码块总会紧跟在try和catch代码块之后执行,但会在try和catch代码块之后的其他代码之前执行。</p>

<p>下面的例子使用了<code>try...catch</code>语句。示例调用了一个函数用于从一个数组中根据传递值来获取一个月份名称。如果该值与月份数值不相符,会抛出一个带有<code>"InvalidMonthNo"</code>值的异常,然后在捕捉块语句中设<code>monthName</code>变量为<code>unknown</code></p>

<pre class="brush: js">function getMonthName(mo) {
  mo = mo - 1; // Adjust month number for array index (1 = Jan, 12 = Dec)
  var months = ["Jan","Feb","Mar","Apr","May","Jun","Jul",
                "Aug","Sep","Oct","Nov","Dec"];
  if (months[mo]) {
    return months[mo];
  } else {
    throw "InvalidMonthNo"; //throw keyword is used here
  }
}

try { // statements to try
  monthName = getMonthName(myMonth); // function could throw exception
}
catch (e) {
  monthName = "unknown";
  logMyErrors(e); // pass exception object to error handler -&gt; your own function
}</pre>

<h4 id="The_catch_Block"><code>catch</code> 块</h4>

<p>你可以使用<code>catch</code>块来处理所有可能在<code>try</code>块中产生的异常。</p>

<pre class="brush: js">catch (catchID) {
  statements
}
</pre>

<p>捕捉块指定了一个标识符(上述语句中的<code>catchID</code>)来存放抛出语句指定的值;你可以用这个标识符来获取抛出的异常信息。在插入<code>throw</code>块时JavaScript创建这个标识符;标识符只存在于<code>catch</code>块的存续期间里;当<code>catch</code>块执行完成时,标识符不再可用。</p>

<p>举个例子,下面代码抛出了一个异常。当异常出现时跳到<code>catch</code>块。</p>

<pre class="brush: js">try {
   throw "myException" // generates an exception
}
catch (e) {
// statements to handle any exceptions
   logMyErrors(e) // pass exception object to error handler
}
</pre>

<h4 id="finally块"><code>finally</code></h4>

<p><code>finally</code>块包含了在try和catch块完成后、下面接着try...catch的语句之前执行的语句。<code>finally</code>块无论是否抛出异常都会执行。如果抛出了一个异常,就算没有异常处理,<code>finally</code>块里的语句也会执行。</p>

<p>你可以用<code>finally</code>块来令你的脚本在异常发生时优雅地退出;举个例子,你可能需要在绑定的脚本中释放资源。接下来的例子用文件处理语句打开了一个文件(服务端的JavaScript允许你进入文件)。如果在文件打开时一个异常抛出,<code>finally</code>块会在脚本错误之前关闭文件。</p>

<pre class="brush: js">openMyFile();
try {
    writeMyFile(theData); //This may throw a error
}catch(e){
    handleError(e); // If we got a error we handle it
}finally {
    closeMyFile(); // always close the resource
}
</pre>

<p>如果<code>finally</code>块返回一个值,该值会是整个<code>try-catch-finally</code>流程的返回值,不管在<code>try</code><code>catch</code>块中语句返回了什么:</p>

<pre class="brush: js">function f() {
  try {
    console.log(0);
    throw "bogus";
  } catch(e) {
    console.log(1);
    return true; // this return statement is suspended
                 // until finally block has completed
    console.log(2); // not reachable
  } finally {
    console.log(3);
    return false; // overwrites the previous "return"
    console.log(4); // not reachable
  }
  // "return false" is executed now
  console.log(5); // not reachable
}
f(); // console 0, 1, 3; returns false

</pre>

<p><code>finally</code>块覆盖返回值也适用于在<code>catch</code>块内抛出或重新抛出的异常:</p>

<pre class="brush: js">function f() {
  try {
    throw 'bogus';
  } catch(e) {
    console.log('caught inner "bogus"');
    throw e; // this throw statement is suspended until
             // finally block has completed
  } finally {
    return false; // overwrites the previous "throw"
  }
  // "return false" is executed now
}

try {
  f();
} catch(e) {
  // this is never reached because the throw inside
  // the catch is overwritten
  // by the return in finally
  console.log('caught outer "bogus"');
}

// OUTPUT
// caught inner "bogus"
</pre>

<h4 id="Nesting_try...catch_Statements">嵌套 try...catch 语句</h4>

<p>你可以嵌套一个或多个<code>try ... catch</code>语句。如果一个内部<code>try ... catch</code>语句没有<code>catch</code>块,它需要有一个<code>finally</code>块,并且封闭的<code>try ... catch</code>语句的<code>catch</code>块被检查匹配。有关更多信息,请参阅<a href="/zh-CN/docs/Web/JavaScript/Reference/Statements/try...catch">try... catch</a>参考页上的<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch#Nested_try-blocks">嵌套try-blocks</a></p>

<h3 id="使用Error对象">使用<code>Error</code>对象</h3>

<p>根据错误类型,你也许可以用'name'和'message'获取更精炼的信息。'name'提供了常规的错误类(如 'DOMException' 或 'Error'),而'message'通常提供了一条从错误对象转换成字符串的简明信息。</p>

<p>在抛出你个人所为的异常时,为了充分利用那些属性(比如你的<code>catch</code>块不能分辨是你个人所为的异常还是系统的异常时),你可以使用 Error 构造函数。比如:</p>

<pre class="brush: js">function doSomethingErrorProne () {
  if (ourCodeMakesAMistake()) {
    throw (new Error('The message'));
  } else {
    doSomethingToGetAJavascriptError();
  }
}
....
try {
  doSomethingErrorProne();
}
catch (e) {
  console.log(e.name); // logs 'Error'
  console.log(e.message); // logs 'The message' or a JavaScript error message)
}</pre>

<h2 id="Promises">Promises</h2>

<p>从 ECMAScript 6 开始,JavaScript 增加了对 {{jsxref("Promise")}} 对象的支持,它允许你对延时和异步操作流进行控制。</p>

<p><code>Promise</code> 对象有以下几种状态:</p>

<ul>
 <li><em>pending:</em>初始的状态,即正在执行,不处于 fulfilled 或 rejected 状态。</li>
 <li><em>fulfilled:</em>成功的完成了操作。</li>
 <li><em>rejected:</em>失败,没有完成操作。</li>
 <li><em>settled:</em>Promise 处于 fulfilled 或 rejected 二者中的任意一个状态, 不会是 pending。</li>
</ul>

<p><img alt="" src="https://mdn.mozillademos.org/files/8633/promises.png"></p>

<h3 id="通过_XHR_加载图片">通过 XHR 加载图片</h3>

<p>你可以在 MDN GitHub <a href="https://github.com/mdn/js-examples/tree/master/promises-test">promise-test</a> 中找到这个简单的例子,它使用了 Promise 和 <code><a href="https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest">XMLHttpRequest</a></code> 来加载一张图片,你也可以直接在<a href="https://mdn.github.io/js-examples/promises-test/">这个页面</a>查看他的效果。同时为了让你更清楚的了解 Promise 和 XHR 的结构,代码中每一个步骤后都附带了注释。</p>

<p>这里有一个未注释的版本,展现了 <code>Promise</code> 的工作流,希望可以对你的理解有所帮助。</p>

<pre class="brush: js">function imgLoad(url) {
  return new Promise(function(resolve, reject) {
    var request = new XMLHttpRequest();
    request.open('GET', url);
    request.responseType = 'blob';
    request.onload = function() {
      if (request.status === 200) {
        resolve(request.response);
      } else {
        reject(Error('Image didn\'t load successfully; error code:'
                     + request.statusText));
      }
    };
    request.onerror = function() {
      reject(Error('There was a network error.'));
    };
    request.send();
  });
}</pre>

<p>若想了解更详细的信息,请参阅 {{jsxref("Promise")}} 页面。</p>

<div>{{PreviousNext("Web/JavaScript/Guide/Grammar_and_types", "Web/JavaScript/Guide/Loops_and_iteration")}}</div>