recursal/MDN huggingface dataset:
block
=====

**块语句**（或其他语言的**复合语句**）用于组合零个或多个语句。该块由一对大括号界定，可以是`labelled`：

语法
--

### 块声明
```

{ StatementList }
```

### 标记块声明
```

LabelIdentifier: { StatementList }
```


`StatementList`

在块语句中分组的语句。

`LabelIdentifier`

用于视觉识别的可选`label`或`break`的目标。

描述
--

其他语言中通常将语句块称为**复合语句**。它允许你使用多个语句，其中 JavaScript 只需要一个语句。将语句组合成块是 JavaScript 中的常见做法。相反的做法是可以使用一个空语句，你不提供任何语句，虽然一个是必需的。

### 块级作用域

#### 在非严格模式 (non-strict mode) 下的`var` 或者函数声明时

通过`var`声明的变量或者非严格模式下 (non-strict mode) 创建的函数声明**没有**块级作用域。在语句块里声明的变量的作用域不仅是其所在的函数或者 script 标签内，所设置变量的影响会在超出语句块本身之外持续存在。换句话说，这种语句块不会引入一个作用域。尽管单独的语句块是合法的语句，但在 JavaScript 中你不会想使用单独的语句块，因为它们不像你想象的 C 或 Java 中的语句块那样处理事物。例如：


```js
var x = 1;
{
  var x = 2;
}
console.log(x); // 输出 2
```


输出结果是 2，因为块中的 `var x`语句与块前面的`var x`语句作用域相同。在 C 或 Java 中，这段代码会输出 1。

#### 使用`let`和 `const`

相比之下，使用 `let`和`const`声明的变量是**有**块级作用域的。


```js
let x = 1;
{
  let x = 2;
}
console.log(x); // 输出 1
```


`x = 2`仅限在定义它的块中。

`const`的结果也是一样的：


```js
const c = 1;
{
  const c = 2;
}
console.log(c); // 输出 1，而且不会报错
```


注意，位于块范围之内的 `const c = 2` 并不会抛出`SyntaxError: Identifier 'c' has already been declared`这样的语法错误，因为在它自己的块中它可能是唯一一个被声明的常量。

##### 使用 let 声明的变量在块级作用域内能强制执行更新变量，下面的两个例子对比：


```js
var a = [];
for (var i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[0](); // 10
a[1](); // 10
a[6](); // 10

/\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*/

var a = [];
for (let i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[0](); // 0
a[1](); // 1
a[6](); // 6
```


#### 使用`function`

函数声明同样被限制在声明他的语句块内：
```

foo('outside');  // TypeError: foo is not a function
{
  function foo(location) {
   console.log('foo is called ' + location);
  }
  foo('inside'); // 正常工作并且打印 'foo is called inside'
}
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-block |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* `while`
* `if...else`
* `let`
debugger
========

**`debugger` 语句**调用任何可用的调试功能，例如设置断点。如果没有调试功能可用，则此语句不起作用。

语法
--


```js
debugger;
```

示例
--

下面的例子演示了一个包含 `debugger` 语句的函数，当函数被调用时，会尝试调用一个可用的调试器进行调试。


```js
function potentiallyBuggyCode() {
  debugger;
  // do potentially buggy stuff to examine, step through, etc.
}
```


当 debugger 被调用时，执行暂停在 `debugger` 语句的位置。就像在脚本源代码中的断点一样。

![开发人员工具打开到调试器面板的浏览器，显示如何在调试器语句处暂停执行，以允许仔细检查变量、作用域、事件等。](/zh-CN/docs/Web/JavaScript/Reference/Statements/debugger/screen_shot_2014-02-07_at_9.14.35_am.png)

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-debugger-statement |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* 在火狐开发者工具中调试
async function\*
================

**`async function*`** 声明定义了一个*异步生成器函数*，其返回一个 `AsyncGenerator` 对象。

尝试一下
----

你也可以使用 `AsyncGeneratorFunction` 构造函数或者 `async function*` 表达式语法定义异步生成器函数。

语法
--


```js
async function\* name(param0) {
  statements
}
async function\* name(param0, param1) {
  statements
}
async function\* name(param0, param1, /\* … ,\*/ paramN) {
  statements
}
```


**备注：** 异步生成器函数没有对应的箭头函数。

### 参数

`name`

函数名。

`param` 可选

函数形式参数的名称。

`statements` 可选

构成函数体的语句。

描述
--

异步生成器函数兼具异步函数和生成器函数的特性。你可以在函数体中使用 `await` 和 `yield` 关键字。这使你能够使用 `await` 方便地处理异步任务，同时利用生成器函数的惰性。

与使用 `function*` 声明的标准生成器函数不同，异步生成器函数返回一个符合异步可迭代协议的 `AsyncGenerator` 对象。每次调用 `next()` 都会返回一个 `Promise`，该 promise 会兑现为迭代器结果对象。

当从异步生成器产生一个 promsie 时，迭代器结果 promise 的最终状态将与生成器产生的 promise 状态相同。例如：


```js
async function\* foo() {
  yield Promise.reject(1);
}

foo()
  .next()
  .catch((e) => console.error(e));
```


因为生成器内部生产的 promise 被拒绝，迭代器的结果也将被拒绝，所以将输出 `1`。异步生成器的兑现结果的 `value` 将不会是另一个 promise。

`async function*` 声明会被提升到其作用域的顶部，并可以在其作用域的任何地方调用。

示例
--

### 声明异步函数

异步生成器函数总是产生结果的 promise——即使每个 `yield` 步骤是同步的。


```js
async function\* myGenerator(step) {
  await new Promise((resolve) => setTimeout(resolve, 10));
  yield 0;
  yield step;
  yield step \* 2;
}

const gen = myGenerator(2);
gen
  .next()
  .then((res) => {
    console.log(res); // { value: 0, done: false }
    return gen.next();
  })
  .then((res) => {
    console.log(res); // { value: 2, done: false }
    return gen.next();
  })
  .then((res) => {
    console.log(res); // { value: 4, done: false }
    return gen.next();
  })
  .then((res) => {
    console.log(res); // { value: undefined, done: true }
    return gen.next();
  });
```

### 使用异步生成器函数去读一系列文件

在这个示例中，我们使用 Node 的 `fs/promises` 模块读取一系列文件并且仅当请求时获取它的内容。


```js
async function\* readFiles(directory) {
  const files = await fs.readdir(directory);
  for (const file of files) {
    const stats = await fs.stat(file);
    if (stats.isFile()) {
      yield {
        name: file,
        content: await fs.readFile(file, "utf8"),
      };
    }
  }
}

const files = readFiles(".");
console.log((await files.next()).value);
// 可能的输出；{ name: 'file1.txt', content: '...' }
console.log((await files.next()).value);
// 可能的输出：{ name: 'file2.txt', content: '...' }
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-async-generator-function-definitions |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* `async function*` 表达式
* `AsyncGeneratorFunction` 对象
* 迭代器协议
* `GeneratorFunction` 对象
* `yield`
* `yield*`
* `Function` 对象
* 函数
for...in
========

**`for...in`** **语句**以任意顺序迭代一个对象的除Symbol以外的可枚举属性，包括继承的可枚举属性。

语法
--
```

for (variable in object)
  statement
```


`variable`

在每次迭代时，variable 会被赋值为不同的属性名。

`object`

非 Symbol 类型的可枚举属性被迭代的对象。

### 数组迭代和 `for...in`

**备注：** `for...in`不应该用于迭代一个关注索引顺序的 `Array`。

### 仅迭代自身的属性

如果你只要考虑对象本身的属性，而不是它的原型，那么使用 `getOwnPropertyNames()` 或执行 `hasOwnProperty()` 来确定某属性是否是对象本身的属性（也能使用`propertyIsEnumerable`）。或者，如果你知道不会有任何外部代码干扰，你可以使用检查方法扩展内置原型。

为什么用 for ... in?
----------------

`for ... in`是为遍历对象属性而构建的，不建议与数组一起使用，数组可以用`Array.prototype.forEach()`和`for ... of`，那么`for ... in`的到底有什么用呢？

它最常用的地方应该是用于调试，可以更方便的去检查对象属性（通过输出到控制台或其他方式）。尽管对于处理存储数据，数组更实用些，但是你在处理有`key-value`数据（比如属性用作“键”），需要检查其中的任何键是否为某值的情况时，还是推荐用`for ... in`。

示例
--

下面的函数接受一个对象作为参数。被调用时迭代传入对象的所有可枚举属性然后返回一个所有属性名和其对应值的字符串。


```js
var obj = { a: 1, b: 2, c: 3 };

for (var prop in obj) {
  console.log("obj." + prop + " = " + obj[prop]);
}

// Output:
// "obj.a = 1"
// "obj.b = 2"
// "obj.c = 3"
```


下面的函数说明了`hasOwnProperty()`的用法：继承的属性不显示。


```js
var triangle = { a: 1, b: 2, c: 3 };

function ColoredTriangle() {
  this.color = "red";
}

ColoredTriangle.prototype = triangle;

var obj = new ColoredTriangle();

for (var prop in obj) {
  if (obj.hasOwnProperty(prop)) {
    console.log(`obj.${prop} = ${obj[prop]}`);
  }
}

// Output:
// "obj.color = red"
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-for-in-and-for-of-statements |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

兼容性：初始化函数表达式
------------

在 SpiderMonkey 40 版本之前，可以在使用一个初始化表达式（`i=0`）在一个`for...in`循环中：


```js
var obj = {a: 1, b: 2, c: 3};
for (var i = 0 in obj) {
  console.log(obj[i]);
}
// 1
// 2
// 3
```


这个非标准行为现在在版本 40 及更高版本中被忽略，并将在严格模式（Firefox bug 748550 和 Firefox bug 1164741）中呈现`SyntaxError`（"for-in loop head declarations may not have initializers"）错误。

像其他引擎 V8(Chrome)，Chakra (IE/Edge)，JSC (WebKit/Safari) 正在研究去除这种不标准的行为。

参见
--

* `for...of` 一个类似的迭代属性值的语句
* `for`
* 属性的可枚举性和所有权
* `Object.getOwnPropertyNames()`
* `Object.hasOwn()`
* `Array.prototype.forEach()`
do...while
==========

**`do...while` 语句**创建一个执行指定语句的循环，直到`condition`值为 false。在执行`statement` 后检测`condition`，所以指定的`statement`至少执行一次。

语法
--
```

do
   statement
while (condition);
```


`statement`

执行至少一次的语句，并在每次 `condition` 值为真时重新执行。想执行多行语句，可使用`block`语句（`{ ... }`）包裹这些语句。

`condition`

循环中每次都会计算的表达式。如果 `condition` 值为真， `statement` 会再次执行。当 `condition` 值为假，则跳到`do...while`之后的语句。

示例
--

### 使用 `do...while`

下面的例子中，`do...while` 循环至少迭代一次，并且继续迭代直到 `i`不再小于 5 时结束。

### HTML 内容


```html
<div id="example"></div>
```

### JavaScript 内容


```js
var result = "";
var i = 0;
do {
  i += 1;
  result += i + " ";
} while (i < 5);
document.getElementById("example").innerHTML = result;
```

### 结果

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-do-while-statement |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* `while`
* `for`
* `break`
* `continue`
const
=====

常量是块级范围的，非常类似用 let 语句定义的变量。但常量的值是无法（通过重新赋值）改变的，也不能被重新声明。

尝试一下
----

语法
--
```

const name1 = value1 [, name2 = value2 [, ... [, nameN = valueN]]];
```


`nameN`

常量名称，可以是任意合法的标识符。

`valueN`

常量值，可以是任意合法的表达式。

描述
--

此声明创建一个常量，其作用域可以是全局或本地声明的块。与`var`变量不同，全局常量不会变为 window 对象的属性。需要一个常数的初始化器；也就是说，你必须在声明的同一语句中指定它的值（这是有道理的，因为以后不能更改）。

**`const` 声明**创建一个值的只读引用。但这并不意味着它所持有的值是不可变的，只是变量标识符不能重新分配。例如，在引用内容是对象的情况下，这意味着可以改变对象的内容（例如，其参数）。

关于“暂存死区”的所有讨论都适用于`let`和`const`。

一个常量不能和它所在作用域内的其他变量或函数拥有相同的名称。

示例
--

### const 基本用法

常量在声明的时候可以使用大小写，但通常情况下全部用大写字母。


```js
// 定义常量 MY\_FAV 并赋值 7
const MY\_FAV = 7;

// 报错 - Uncaught TypeError: Assignment to constant variable.
MY\_FAV = 20;

// MY\_FAV is 7
console.log("my favorite number is: " + MY\_FAV);

// 尝试重新声明会报错
// Uncaught SyntaxError: Identifier 'MY\_FAV' has already been declared
const MY\_FAV = 20;

// MY\_FAV 保留给上面的常量，这个操作会失败
var MY\_FAV = 20;

// 也会报错
let MY\_FAV = 20;
```

### 块作用域

注意块作用域的性质很重要


```js
if (MY\_FAV === 7) {
  // 没问题，并且创建了一个块作用域变量 MY\_FAV
  // (works equally well with let to declare a block scoped non const variable)
  let MY\_FAV = 20;

  // MY\_FAV 现在为 20
  console.log("my favorite number is " + MY\_FAV);

  // 这被提升到全局上下文并引发错误
  var MY\_FAV = 20;
}

// MY\_FAV 依旧为 7
console.log("my favorite number is " + MY\_FAV);
```

### 常量要求一个初始值


```js
// 报错
// Uncaught SyntaxError: Missing initializer in const declaration

const FOO;
```

### 常量可以定义成对象和数组

常量可以定义成对象和数组


```js
const MY\_OBJECT = { key: "value" };

// 重写对象和上面一样会失败
// Uncaught TypeError: Assignment to constant variable.
MY\_OBJECT = { OTHER\_KEY: "value" };

// 对象属性并不在保护的范围内
// 下面这个声明会成功执行
MY\_OBJECT.key = "otherValue"; // Use Object.freeze() to make object immutable

// 也可以用来定义数组
const MY\_ARRAY = [];
// 可以向数组填充数据
MY\_ARRAY.push("A"); // ["A"]
// 但是，将一个新数组赋给变量会引发错误
// Uncaught TypeError: Assignment to constant variable.
MY\_ARRAY = ["B"];
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-let-and-const-declarations |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* `var`
* `let`
* JavaScript 指南中的常量
function
========

**函数声明**定义一个具有指定参数的函数。

你还可以使用 `Function` 构造函数和 一个`function expression` 定义函数。

语法
--
```

function name([param,[, param,[..., param]]]) {
   [statements]
}
```


`name`

函数名

`param`

要传递给函数的参数的名称。不同引擎中的最大参数数量不同。

`statements`

包含函数体的语句。

描述
--

一个被函数声明创建的函数是一个 Function 对象，具有 Function 对象的所有属性、方法和行为。查看 Function 以获取 function 的详细信息。

函数也可以被表达式创建（ function expression ）

函数可以被有条件来声明，这意味着，在一个 if 语句里，函数声明是可以嵌套的。有的浏览器会将这种有条件的声明看成是无条件的声明，无论这里的条件是 true 还是 false，浏览器都会创建函数。因此，它们不应该被使用。

默认情况下，函数是返回 undefined 的。想要返回一个其他的值，函数必须通过一个 return 语句指定返回值。

### 有条件的创建函数

函数可以被有条件来声明，这意味着，函数声明可能出现在一个 if 语句里，但是，这种声明方式在不同的浏览器里可能有不同的效果。因此，不应该在生产环境代码中使用这种声明方式，应该使用函数表达式来代替。


```js
var hoisted = "foo" in this;
console.log(
  `'foo' name ${
 hoisted ? "is" : "is not"
 } hoisted. typeof foo is ${typeof foo}`,
);
if (false) {
  function foo() {
    return 1;
  }
}

// 在 Chrome 里：
// 'foo' 变量名被提升，但是 typeof foo 为 undefined
//
// 在 Firefox 里：
// 'foo' 变量名被提升。但是 typeof foo 为 undefined
//
// 在 Edge 里：
// 'foo' 变量名未被提升。而且 typeof foo 为 undefined
//
// 在 Safari 里：
// 'foo' 变量名被提升。而且 typeof foo 为 function
```


注意，即使把上面代码中的 if(false) 改为 if(true)，结果也是一样的


```js
var hoisted = "foo" in this;
console.log(
  `'foo' name ${
 hoisted ? "is" : "is not"
 } hoisted. typeof foo is ${typeof foo}`,
);
if (true) {
  function foo() {
    return 1;
  }
}

// 在 Chrome 里：
// 'foo' 变量名被提升，但是 typeof foo 为 undefined
//
// 在 Firefox 里：
// 'foo' 变量名被提升。但是 typeof foo 为 undefined
//
// 在 Edge 里：
// 'foo' 变量名未被提升。而且 typeof foo 为 undefined
//
// 在 Safari 里：
// 'foo' 变量名被提升。而且 typeof foo 为 function
```

### 函数声明提升

JavaScript 中的**函数声明**被提升到了**函数定义**。你可以在函数声明之前使用该函数：


```js
hoisted(); // logs "foo"

function hoisted() {
  console.log("foo");
}
```


**备注：** 函数表达式 `function expressions` 不会被提升：


```js
notHoisted(); // TypeError: notHoisted is not a function

var notHoisted = function () {
  console.log("bar");
};
```

示例
--

### 使用函数

下面的代码声明了一个函数，该函数返回了销售的总金额，参数是产品 a,b,c 分别的销售的数量。


```js
function calc\_sales(units\_a, units\_b, units\_c) {
  return units_a \* 79 + units_b \* 129 + units_c \* 699;
}
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-function-definitions |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* 函数指南
* Function
* `Function`
* `function` 表达式
* `function*`
* `async function`
* `async function*`
class
=====

Baseline Widely available
-------------------------

This feature is well established and works across many devices and browser versions. It’s been available across browsers since March 2017.

* Learn more
* See full compatibility
* Report feedback
**class 声明**创建一个基于原型继承的具有给定名称的新类。

尝试一下
----

你也可以使用类表达式定义类。但是不同于类表达式，类声明不允许再次声明已经存在的类，否则将会抛出一个类型错误。

语法
--


```js
class name [extends] {
  // class body
}
```

描述
--

和类表达式一样，类声明体在严格模式下运行。构造函数是可选的。

类声明不可以提升（这与函数声明不同）。

示例
--

### 声明一个类

在下面的例子中，我们首先定义一个名为 Polygon 的类，然后继承它来创建一个名为 Square 的类。注意，构造函数中使用的 super() 只能在构造函数中使用，并且必须在使用 this 关键字前调用。


```js
class Polygon {
  constructor(height, width) {
    this.name = "Polygon";
    this.height = height;
    this.width = width;
  }
}

class Square extends Polygon {
  constructor(length) {
    super(length, length);
    this.name = "Square";
  }
}
```


**警告：**

### 重复定义类

重复声明一个类会引起类型错误。


```js
class Foo {}
class Foo {}
// Uncaught TypeError: Identifier 'Foo' has already been declared
```


若之前使用类表达式定义了一个类，则再次声明这个类同样会引起类型错误。


```js
let Foo = class {};
class Foo {}
// Uncaught TypeError: Identifier 'Foo' has already been declared
```


规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-class-definitions |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

See also
--------

* `function` declaration
* `class` expression
* Classes
label
=====

**标记语句**可以和 `break` 或 `continue` 语句一起使用。标记就是在一条语句前面加个可以引用的标识符（identifier）。

尝试一下
----

**备注：** 使用标记的循环或语句块非常罕见。通常情况下，可以使用函数调用而不是（基于标记的）循环跳转。

语法
--
```

label :
   statement
```


`label`

任何不属于保留关键字的 JavaScript 标识符。

`statement`

JavaScript 语句。`break` 可用于任何标记语句，而 `continue` 可用于循环标记语句。

描述
--

可使用一个标签来唯一标记一个循环，然后使用 `break` 或 `continue` 语句来指示程序是否中断循环或继续执行。

需要注意的是，JavaScript 没有 `goto` 语句，标记只能和 `break` 或 `continue` 一起使用。

在严格模式中，你不能使用“`let`”作为标签名称。它会抛出一个 `SyntaxError`（因为 let 是一个保留的标识符）。

示例
--

### 在 `for` 循环中使用带标记的 `continue` 语句


```js
var i, j;

loop1: for (i = 0; i < 3; i++) {
  //The first for statement is labeled "loop1"
  loop2: for (j = 0; j < 3; j++) {
    //The second for statement is labeled "loop2"
    if (i === 1 && j === 1) {
      continue loop1;
    }
    console.log("i = " + i + ", j = " + j);
  }
}

// Output is:
// "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"
// Notice how it skips both "i = 1, j = 1" and "i = 1, j = 2"
```

### 使用带标记的 `continue` 语句

若给定一个数据数组和一个测试数组，则下面的例子会统计通过测试的数据的数量。


```js
var itemsPassed = 0;
var i, j;

top: for (i = 0; i < items.length; i++) {
  for (j = 0; j < tests.length; j++) {
    if (!tests[j].pass(items[i])) {
      continue top;
    }
  }

  itemsPassed++;
}
```

### 在 `for` 循环中使用带标记的 `break`


```js
var i, j;

loop1: for (i = 0; i < 3; i++) {
  //The first for statement is labeled "loop1"
  loop2: for (j = 0; j < 3; j++) {
    //The second for statement is labeled "loop2"
    if (i == 1 && j == 1) {
      break loop1;
    }
    console.log("i = " + i + ", j = " + j);
  }
}

// Output is:
// "i = 0, j = 0"
// "i = 0, j = 1"
// "i = 0, j = 2"
// "i = 1, j = 0"
// Notice the difference with the previous continue example
```

### 使用带标记的 `break` 语句

若给定一个数据数组和一个测试数组，则下面的例子会判断是否所有数据均通过了测试。


```js
var allPass = true;
var i, j;

top: for (i = 0; items.length; i++)
  for (j = 0; j < tests.length; i++)
    if (!tests[j].pass(items[i])) {
      allPass = false;
      break top;
    }
```

### 在标记块中使用 `break`

你可以在代码块中使用标记，但只有 `break` 语句可以使用非循环标记。


```js
foo: {
  console.log("face");
  break foo;
  console.log("this will not be executed");
}
console.log("swap");

// this will log:

// "face"
// "swap
```

### 标记函数声明

从 ECMAScript 2015 开始，标准的函数声明现在对规范的 Web 兼容性附件中的非严格代码进行了标准化。


```js
L: function F() {}
```


在严格模式中，这会抛出 `SyntaxError`：


```js
"use strict";
L: function F() {}
// SyntaxError: functions cannot be labelled
```


无论是否处于严格模式下，生成器函数都不能被标记：


```js
L: function\* F() {}
// SyntaxError: generator functions cannot be labelled
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-labelled-statements |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* `break`
* `continue`
构造方法
====

Baseline Widely available
-------------------------

This feature is well established and works across many devices and browser versions. It’s been available across browsers since March 2017.

* Learn more
* See full compatibility
* Report feedback
**`constructor`** 是一种用于创建和初始化`class`创建的对象的特殊方法。

尝试一下
----

语法
--
```

constructor([arguments]) { ... }
```

描述
--

在一个类中只能有一个名为“constructor”的特殊方法。一个类中出现多次构造函数 (`constructor)`方法将会抛出一个 `SyntaxError` 错误。

在一个构造方法中可以使用`super`关键字来调用一个父类的构造方法。

如果没有显式指定构造方法，则会添加默认的 constructor 方法。

如果不指定一个构造函数 (constructor) 方法，则使用一个默认的构造函数 (constructor)。

示例
--

### 使用`constructor`方法

以下代码片段来自 类的实例（在线 demo）。


```js
class Square extends Polygon {
  constructor(length) {
    // 在这里，它调用了父类的构造函数，并将 lengths 提供给 Polygon 的"width"和"height"
    super(length, length);
    // 注意：在派生类中，必须先调用 super() 才能使用 "this"。
    // 忽略这个，将会导致一个引用错误。
    this.name = "Square";
  }
  get area() {
    return this.height \* this.width;
  }
  set area(value) {
    // 注意：不可使用 this.area = value
    // 否则会导致循环 call setter 方法导致爆栈
    this._area = value;
  }
}
```

### 另一个例子

看看这个代码片段


```js
class Polygon {
  constructor() {
    this.name = "Polygon";
  }
}

class Square extends Polygon {
  constructor() {
    super();
  }
}

class Rectangle {}

Object.setPrototypeOf(Square.prototype, Rectangle.prototype);

console.log(Object.getPrototypeOf(Square.prototype) === Polygon.prototype); //false
console.log(Object.getPrototypeOf(Square.prototype) === Rectangle.prototype); //true

let newInstance = new Square();
console.log(newInstance.name); //Polygon
```


这里，**Square**类的原型被改变，但是在正在创建一个新的正方形实例时，仍然调用前一个基类**Polygon**的构造函数。

### 默认构造方法

如前所述，如果不指定构造方法，则使用默认构造函数。对于基类，默认构造函数是：


```js
constructor() {}
```


对于派生类，默认构造函数是：


```js
constructor(...args) {
  super(...args);
}
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-static-semantics-constructormethod |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参阅
--

* super()
* `class` expression
* `class` declaration
* Classes
extends
=======

Baseline Widely available
-------------------------

This feature is well established and works across many devices and browser versions. It’s been available across browsers since March 2017.

* Learn more
* See full compatibility
* Report feedback
**`extends`** 关键字用于类声明或者类表达式中，以创建一个类，该类是另一个类的子类。

语法
--
```

class ChildClass extends ParentClass { ... }
```

描述
--

`extends` 关键字用来创建一个普通类或者内建对象的子类。

继承的 `.prototype` 必须是一个 `Object` 或者 `null`。

示例
--

### 使用 `extends`

第一个例子是根据名为 `Polygon` 类创建一个名为 `Square` 的类。这个例子是从这个在线演示中提取出来的。


```js
class Square extends Polygon {
  constructor(length) {
    // Here, it calls the parent class' constructor with lengths
    // provided for the Polygon's width and height
    super(length, length);
    // Note: In derived classes, super() must be called before you
    // can use 'this'. Leaving this out will cause a reference error.
    this.name = "Square";
  }

  get area() {
    return this.height \* this.width;
  }
}
```

### 使用 `extends`与内置对象

这个示例继承了内置的 `Date` 对象。这个例子是从这个在线演示中提取出来的。


```js
class myDate extends Date {
  constructor() {
    super();
  }

  getFormattedDate() {
    var months = [
      "Jan",
      "Feb",
      "Mar",
      "Apr",
      "May",
      "Jun",
      "Jul",
      "Aug",
      "Sep",
      "Oct",
      "Nov",
      "Dec",
    ];
    return (
      this.getDate() + "-" + months[this.getMonth()] + "-" + this.getFullYear()
    );
  }
}
```

### 扩展 `null`

可以像扩展普通类一样扩展 `null`，但是新对象的原型将不会继承 `Object.prototype` (en-US)。


```js
class nullExtends extends null {
  constructor() {}
}

Object.getPrototypeOf(nullExtends); // Function.prototype
Object.getPrototypeOf(nullExtends.prototype); // null

new nullExtends(); //ReferenceError: this is not defined
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-class-definitions |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

扩展阅读
----

* 类
* super
static
======

Baseline Widely available
-------------------------

This feature is well established and works across many devices and browser versions. It’s been available across browsers since March 2017.

* Learn more
* See full compatibility
* Report feedback
类（class）通过 **static** 关键字定义静态方法。不能在类的实例上调用静态方法，而应该通过类本身调用。这些通常是实用程序方法，例如创建或克隆对象的功能。

尝试一下
----

语法
--
```

static methodName() { ... }
```

描述
--

静态方法调用直接在类上进行，不能在类的实例上调用。静态方法通常用于创建实用程序函数。

调用静态方法
------

### 从另一个静态方法

静态方法调用同一个类中的其他静态方法，可使用 `this` 关键字。


```js
class StaticMethodCall {
  static staticMethod() {
    return "Static method has been called";
  }
  static anotherStaticMethod() {
    return this.staticMethod() + " from another static method";
  }
}
StaticMethodCall.staticMethod();
// 'Static method has been called'

StaticMethodCall.anotherStaticMethod();
// 'Static method has been called from another static method'
```

### 从类的构造函数和其他方法

非静态方法中，不能直接使用 `this` 关键字来访问静态方法。而是要用类名来调用：`CLASSNAME.STATIC_METHOD_NAME()` ，或者用构造函数的属性来调用该方法： `this.constructor.STATIC_METHOD_NAME()`.


```js
class StaticMethodCall {
  constructor() {
    console.log(StaticMethodCall.staticMethod());
    // 'static method has been called.'
    console.log(this.constructor.staticMethod());
    // 'static method has been called.'
  }
  static staticMethod() {
    return "static method has been called.";
  }
}
```

示例
--

下面的例子说明了这几点：

1. 静态方法如何在类上实现。
2. 具有静态成员的类，可以被子类化。
3. 什么情况下静态方法可以调用，什么情况下不能调用。


```js
class Tripple {
  static tripple(n = 1) {
    return n \* 3;
  }
}

class BiggerTripple extends Tripple {
  static tripple(n) {
    return super.tripple(n) \* super.tripple(n);
  }
}

console.log(Tripple.tripple()); // 3
console.log(Tripple.tripple(6)); // 18

let tp = new Tripple();

console.log(BiggerTripple.tripple(3)); // 81（不会受父类实例化的影响）
console.log(tp.tripple()); // 'tp.tripple 不是一个函数'.
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-class-definitions |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* `class` 表达式
* `class` 声明
* 类
InternalError: too much recursion
=================================

信息
--
```

InternalError: too much recursion
```

错误类型
----

`内部错误`.

什么地方出错了？
--------

一个调用自身的函数被称作*递归函数*。一些情况下，递归函数类似于一个循环，都重复地执行一个代码段许多次，都需要一个条件（用于避免无尽循环或此处的无尽递归）。当出现过于深层的递归或无尽递归时，JavaScript 将会抛出此错误。

示例
--

根据递归终止的条件，该函数将递归地执行 10 次。


```js
function loop(x) {
  if (x >= 10) {
    // "x >= 10" 是递归终止条件
    return;
  }
  // 进行一些操作...
  loop(x + 1); // 递归调用
}
loop(0);
```


将递归条件设置为一个极大的数值，将不能运行：


```js
function loop(x) {
  if (x >= 1000000000000) {
    return;
  }
  // 进行一些操作...
  loop(x + 1);
}
loop(0);

// InternalError: too much recursion
```

相关页面
----

* 递归
* Recursive functions