Function() 构造函数 - JavaScript

Function() 构造函数
===============

**`Function()`** 构造函数创建 `Function` 对象。直接调用构造函数可以动态创建函数，但可能会面临一些安全性和类似于 `eval()` 的性能问题（但相对较小）。然而，与具有访问本地作用域的 `eval` 不同，`Function` 构造函数创建的函数仅在全局作用域中执行。

尝试一下
----

语法
--

```js
new Function(functionBody)
new Function(arg0, functionBody)
new Function(arg0, arg1, functionBody)
new Function(arg0, arg1, /\* …, \*/ argN, functionBody)

Function(functionBody)
Function(arg0, functionBody)
Function(arg0, arg1, functionBody)
Function(arg0, arg1, /\* …, \*/ argN, functionBody)
```

**备注：** 调用 `Function()` 时可以使用或不使用 `new`。两者都会创建一个新的 `Function` 实例。

### 参数

`argN` 可选

被函数用作形参的名称。每个名称都必须是字符串，对应于一个有效的 JavaScript 参数（任何一个普通的标识符、剩余参数或解构参数，可选择使用默认参数），或用逗号分隔的此类字符串的列表。

由于参数的解析方式与函数表达式的解析方式相同，所以接受空白和注释。例如：`"x", "theValue = 42", "[a, b] /* numbers */"` 或 `"x, theValue = 42, [a, b] /* numbers */"`。（`"x, theValue = 42", "[a, b]"` 也是正确的，虽然有些难以阅读。）

`functionBody`

一个包含构成函数定义的 JavaScript 语句的字符串。

描述
--

使用 `Function` 构造函数创建的 `Function` 对象会在函数创建时完成解析。这比用函数表达式或函数声明创建一个函数并在代码中调用它的效率要低，因为使用表达式或声明创建的函数会和其他的代码一起被解析。

传递给函数的所有参数，除了最后一个，都被当作要创建的函数中参数的标识符的名称，按照它们被传递的顺序进行处理。该函数将被动态编译为一个函数表达式，其源代码以下列方式组装：

```js
`function anonymous(${args.join(",")}
) {
${functionBody}
}`;
```

这可以通过调用函数的 `toString()` 方法来观察。

然而，与普通的函数表达式不同，`anonymous` 这个名字不会被添加到 `functionBody` 的作用域中，因为 `functionBody` 只能访问全局作用域。如果 `functionBody` 不在严格模式中（主体本身需要有 `"use strict"` 指令，因为它不从上下文中继承严格性），你可以使用 `arguments.callee` 来指代函数本身。另外，你可以将递归部分定义为一个内部函数：

```js
const recursiveFn = new Function(
  "count",
  `
(function recursiveFn(count) {
 if (count < 0) {
 return;
 }
 console.log(count);
 recursiveFn(count - 1);
})(count);
`,
);
```

请注意，集合源的两个动态部分——参数列表 `args.join(",")` 和 `functionBody` 将首先被分别解析，以确保它们在语法上都是有效的。这可以防止类似注入的尝试。

```js
new Function("/\*", "\*/) {");
// SyntaxError: Unexpected end of arg string
// 不会变成 "function anonymous(/\*) {\*/) {}"
```

示例
--

### 与 Function 构造函数一同指定参数

以下代码创建了一个接受两个参数的 `Function` 对象。

```js
// 此示例可在你的 JavaScript 控制台下运行

// 创建一个接受两个参数的函数，并返回这些参数的和
const adder = new Function("a", "b", "return a + b");

// 调用函数
adder(2, 6);
// 8
```

参数 `a` 和 `b` 是在函数体 `return a + b` 中使用的正式参数名称。

### 从函数声明或函数表达式创建一个函数对象

```js
// 函数构造器可以接受多个分号分隔的语句。Function 表达式需要带有函数的返回语句

// 观察一下，new Function 被调用了。这样我们就可以在事后直接调用我们创建的函数了
const sumOfArray = new Function(
  "const sumArray = (arr) => arr.reduce((previousValue, currentValue) => previousValue + currentValue); return sumArray",
)();

// 调用函数
sumOfArray([1, 2, 3, 4]);
// 10

// 如果你不在创建函数时调用 new Function，你可以使用 Function.call() 方法来调用它
const findLargestNumber = new Function(
  "function findLargestNumber (arr) { return Math.max(...arr) }; return findLargestNumber",
);
// 调用函数
findLargestNumber.call({}).call({}, [2, 4, 1, 8, 5]);
// 8

// 函数声明不需要返回语句
const sayHello = new Function(
  "return function (name) { return `Hello, ${name}` }",
)();

// 调用函数
sayHello("world");
// Hello, world
```

规范
--

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

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

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

参见
--

* 函数声明
* 函数表达式
* 函数


Function.prototype.call() - JavaScript

Function.prototype.call()
=========================

`Function` 实例的 **`call()`** 方法会以给定的 `this` 值和逐个提供的参数调用该函数。

尝试一下
----

语法
--

```js
call(thisArg)
call(thisArg, arg1)
call(thisArg, arg1, arg2)
call(thisArg, arg1, arg2, /\* …, \*/ argN)
```

### 参数

`thisArg`

在调用 `func` 时要使用的 `this` 值。如果函数不在严格模式下，`null` 和 `undefined` 将被替换为全局对象，并且原始值将被转换为对象。

`arg1, …, argN` 可选

函数的参数。

返回值
---

使用指定的 `this` 值和参数调用函数后的结果。

描述
--

**备注：** 这个函数几乎与 `apply()` 相同，只是函数的参数以列表的形式逐个传递给 `call()`，而在 `apply()` 中它们被组合在一个对象中，通常是一个数组——例如，`func.call(this, "eat", "bananas")` 与 `func.apply(this, ["eat", "bananas"])`。

通常，在调用函数时，函数内部的 `this` 值是访问该函数的对象。使用 `call()`，你可以在调用现有函数时将任意值分配给 `this`，而无需首先将函数附加到对象上作为属性。这样可以将一个对象的方法用作通用的实用函数。

**警告：** 不要使用 `call()` 来链式调用构造函数（例如，实现继承）。这会将构造函数作为普通函数调用，这意味着 `new.target` 的值为 `undefined`，而类会抛出错误，因为它们不能在没有 `new` 的情况下被调用。请改用 `Reflect.construct()` 或 `extends`。

示例
--

### 使用 call() 调用函数并指定 this 值

在下面的示例中，当我们调用 `greet` 时，`this` 的值将绑定到对象 `obj`，即使 `greet` 不是 `obj` 的方法。

```js
function greet() {
  console.log(this.animal, "的睡眠时间一般在", this.sleepDuration, "之间");
}

const obj = {
  animal: "猫",
  sleepDuration: "12 到 16 小时",
};

greet.call(obj); // 猫 的睡眠时间一般在 12 到 16 小时 之间
```

### 使用 call() 在不指定第一个参数的情况下调用函数

如果省略第一个 `thisArg` 参数，则默认为 `undefined`。在非严格模式下，`this` 值将被替换为 `globalThis`（类似于全局对象）。

```js
globalThis.globProp = "Wisen";

function display() {
  console.log(`globProp 的值是 ${this.globProp}`);
}

display.call(); // 输出“globProp 的值是 Wisen”
```

在严格模式下，`this` 的值不会被替换，因此它保持为 `undefined`。

```js
"use strict";

globalThis.globProp = "Wisen";

function display() {
  console.log(`globProp 的值时 ${this.globProp}`);
}

display.call(); // 抛出 TypeError: Cannot read the property of 'globProp' of undefined
```

### 将方法转换为实用函数

`call()` 几乎等同于普通函数调用，只是将 `this` 作为普通参数传入，而不是作为函数所在的对象值。这类似于通用的实用函数的工作方式：你可以使用 `map(array, callback)` 来代替 `array.map(callback)`，这样可以避免对 `Array.prototype` 进行修改，还可以将 `map` 用于不是数组的类数组对象（例如 `arguments`）。

以 `Array.prototype.slice()` 为例，你想要将类数组对象转换为真正的数组。你可以创建一个类似这样的快捷方式：

```js
const slice = Array.prototype.slice;

// ...

slice.call(arguments);
```

请注意，你不能将 `slice.call` 保存并将其作为普通函数调用，因为 `call()` 方法也会读取它的 `this` 值，而这个值应该是它要调用的函数。在这种情况下，你可以使用 `bind()` 来绑定 `call()` 的 `this` 值。在下面的代码片段中，`slice()` 是一个绑定了 `this` 值为 `Array.prototype.slice()` 的 `Function.prototype.call()` 的版本。这意味着额外的 `call()` 调用可以被省略：

```js
// 与前面示例中的“slice”相同
const unboundSlice = Array.prototype.slice;
const slice = Function.prototype.call.bind(unboundSlice);

// ...

slice(arguments);
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-function.prototype.call |

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

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

参见
--

* `Function.prototype.bind()`
* `Function.prototype.apply()`
* `Reflect.apply()`
* 展开语法
* JavaScript 对象入门


Function.prototype.arguments - JavaScript

Function.prototype.arguments
============================

**已弃用:** 不再推荐使用该特性。虽然一些浏览器仍然支持它，但也许已从相关的 web 标准中移除，也许正准备移除或出于兼容性而保留。请尽量不要使用该特性，并更新现有的代码；参见本页面底部的兼容性表格以指导你作出决定。请注意，该特性随时可能无法正常工作。

**非标准:** 该特性是非标准的，请尽量不要在生产环境中使用它！

**备注：** `Function` 对象的 `arguments` 属性已被弃用。访问 `arguments` 对象的推荐方式是在函数内部引用可用的 `arguments` 变量。

`Function` 实例的 **`arguments`** 访问器属性返回传递给该函数的参数。对于严格模式、箭头函数、异步函数和生成器函数，访问 `arguments` 属性会抛出 `TypeError`。

描述
--

`arguments` 的值是一个类数组对象，对应于传入函数的参数。

在递归的情况下，即如果函数 `f` 在调用栈上出现多次，`f.arguments` 的值表示最近一次调用该函数时的参数。

如果函数没有正在进行的、未完成的调用（即函数已被调用但尚未返回），`arguments` 属性的值通常为 `null`。

请注意，ECMAScript 规范只规定了 `Function.prototype` 具有一个初始的 `arguments` 访问器，对于任何 `get` 或 `set` 请求都无条件地抛出 `TypeError`（称为“毒丸访问器”），而且引擎实现不允许改变此语义，除非是非严格的普通函数。（对于非严格的普通函数）`arguments` 属性的实际行为如果不是抛出错误，则该行为由实现定义。例如，Chrome 将其定义为自有的数据属性，而 Firefox 和 Safari 扩展了初始的毒丸访问器 `Function.prototype.arguments`，以特殊处理非严格函数的 `this` 值。

```js
(function f() {
  if (Object.hasOwn(f, "arguments")) {
    console.log(
      "arguments 是带有描述符的自有属性",
      Object.getOwnPropertyDescriptor(f, "arguments"),
    );
  } else {
    console.log(
      "f 没有自有的名为 arguments 的属性。尝试获取 f.[[Prototype]].arguments",
    );
    console.log(
      Object.getOwnPropertyDescriptor(
        Object.getPrototypeOf(f),
        "arguments",
      ).get.call(f),
    );
  }
})();

// 在 Chrome 中：
// arguments 是带有描述符的自有属性 {value: Arguments(0), writable: false, enumerable: false, configurable: false}

// 在 Firefox 中：
// f 没有自有的名为 arguments 的属性。尝试获取 f.[[Prototype]].arguments
// Arguments { … }
```

示例
--

### 使用 arguments 属性

```js
function f(n) {
  g(n - 1);
}

function g(n) {
  console.log(`之前：${g.arguments[0]}`);
  if (n > 0) {
    f(n);
  }
  console.log(`之后：${g.arguments[0]}`);
}

f(2);

console.log(`返回：${g.arguments}`);

// 输出：
// 之前：1
// 之前：0
// 之后：0
// 之后：1
// 返回：null
```

规范
--

不属于任何规范。

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

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

参见
--

* `arguments`
* 函数


Function.prototype.toString() - JavaScript

Function.prototype.toString()
=============================

`Function` 实例的 **`toString()`** 方法返回一个表示该函数源码的字符串。

尝试一下
----

语法
--

```js
toString()
```

### 参数

无。

### 返回值

一个表示函数源代码的字符串。

描述
--

`Function` 对象重写了从 `Object` 继承来的 `toString()` 方法，而没有继承 `toString`。对于用户定义的 `Function` 对象，`toString` 方法返回一个字符串，其中包含用于定义函数的源文本段。

当 `Function` 需要表示为文本值时，JavaScript 会自动调用函数的 `toString` 方法，例如：函数与一个字符串进行拼接。

若 `this` 不是 `Function` 对象，则 `toString()` 方法将抛出 `TypeError` 异常（"Function.prototype.toString called on incompatible object"）。

```js
Function.prototype.toString.call("foo"); // 抛出 TypeError
```

如果在内置函数对象上调用 `toString()` 方法，或者在由 `Function.prototype.bind()` 创建的函数以及在其他非 JavaScript 函数上调用 `toString()`，那么 `toString()` 将返回一个看起来*像原函数的字符串*，类似于：
```

function someName() { [native code] }
```

对于内部对象方法和函数，`someName` 是函数的初始名称；否则其可能是实现定义（implementation-defined）的，但始终以属性名称语法的形式呈现，如：`[1 + 1]`、`someName` 或 `1`。

**备注：** 这意味着对原生函数的字符串调用 `eval()` 将始终产生语法错误。

若是在由 `Function` 构造函数生成的函数上调用 `toString()`，则 `toString()` 返回创建后的函数源码，包括形参和函数体，函数名为“anonymous”。例如：对于 `Function("a", "b", "return a + b").toString()`，则会返回：
```

function anonymous(a,b
) {
return a + b
}
```

从 ES2018 开始，规范要求 `toString()` 的返回值与声明的源代码完全相同，包括空格和注释；或者因某种原因，主机没有源代码，则要求返回一个原生函数字符串。参见兼容性表格以查询对这一修改后的行为的支持情况。

示例
--

### 比较实际源代码与 toString 的结果

```js
function test(fn) {
  console.log(fn.toString());
}

function f() {}
class A {
  a() {}
}
function\* g() {}

test(f); // "function f() {}"
test(A); // "class A { a() {} }"
test(g); // "function\* g() {}"
test((a) => a); // "(a) => a"
test({ a() {} }.a); // "a() {}"
test({ \*a() {} }.a); // "\*a() {}"
test({ [0]() {} }[0]); // "[0]() {}"
test(Object.getOwnPropertyDescriptor({ get a() {} }, "a").get); // "get a() {}"
test(Object.getOwnPropertyDescriptor({ set a(x) {} }, "a").set); // "set a(x) {}"
test(Function.prototype.toString); // "function toString() { [native code] }"
test(function f() {}.bind(0)); // "function () { [native code] }"
test(Function("a", "b")); // function anonymous(a\n) {\nb\n}
```

注意：在 `Function.prototype.toString()` 的行为变更后，在调用 `toString()` 时，不允许对该方法的实现在其不是原生函数的字符串时合成函数的源码。方法始终返回创建函数时使用的确切的源代码——包括以上示例中的 getter 和 setter。`Function` 构造函数本身具有合成函数源代码的能力（即，一种隐式的 `eval()` 形式）。

### 获取函数的源文本

可以通过将函数强制转换为字符串来获取函数的源文本——例如，通过将其包装在模板字符串中：

```js
function foo() {
  return "bar";
}
console.log(`${foo}`);
// function foo() {
// return "bar";
// }
```

得到的源文本是*准确的*，包括其中的注释（否则引擎的内部表示不会存储这些注释）。

```js
function foo /\* a comment \*/() {
  return "bar";
}
console.log(foo.toString());
// function foo /\* a comment \*/() {
// return "bar";
// }
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-function.prototype.tostring |

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

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

参见
--

* `Object.prototype.toString()`


Function.prototype.apply() - JavaScript

Function.prototype.apply()
==========================

`Function` 实例的 **`apply()`** 方法会以给定的 `this` 值和作为数组（或类数组对象）提供的 `arguments` 调用该函数。

尝试一下
----

语法
--

```js
apply(thisArg)
apply(thisArg, argsArray)
```

### 参数

`thisArg`

调用 `func` 时提供的 `this` 值。如果函数不处于严格模式，则 `null` 和 `undefined` 会被替换为全局对象，原始值会被转换为对象。

`argsArray` 可选

一个类数组对象，用于指定调用 `func` 时的参数，或者如果不需要向函数提供参数，则为 `null` 或 `undefined`。

### 返回值

使用指定的 `this` 值和参数调用函数的结果。

描述
--

**备注：** 这个函数与 `call()` 几乎完全相同，只是函数参数在 `call()` 中逐个作为列表传递，而在 `apply()` 中它们会组合在一个对象中，通常是一个数组——例如，`func.call(this, "eat", "bananas")` 与 `func.apply(this, ["eat", "bananas"])`。

通常情况下，在调用函数时，函数内部的 `this` 的值是访问该函数的对象。使用 `apply()`，你可以在调用现有函数时将任意值分配给 `this`，而无需先将函数作为属性附加到对象上。这使得你可以将一个对象的方法用作通用的实用函数。

你还可以使用任何类数组对象作为第二个参数。实际上，这意味着它需要具有 `length` 属性，并且整数（“索引”）属性的范围在 `(0..length - 1)` 之间。例如，你可以使用一个 `NodeList`，或者像 `{ 'length': 2, '0': 'eat', '1': 'bananas' }` 这样的自定义对象。你还可以使用 `arguments`，例如：

```js
function wrapper() {
  return anotherFn.apply(null, arguments);
}
```

使用剩余参数和参数的展开语法，可以重写为：

```js
function wrapper(...args) {
  return anotherFn(...args);
}
```

一般而言，`fn.apply(null, args)` 等同于使用参数展开语法的 `fn(...args)`，只是在前者的情况下，`args` 期望是类数组对象，而在后者的情况下，`args` 期望是可迭代对象。

**警告：** 不要使用 `apply()` 进行构造函数链式调用（例如，实现继承）。这会将构造函数作为普通函数调用，这意味着 `new.target` 是 `undefined`，从而类会抛出错误，因为它们不能在没有 `new` 的情况下调用。请改用 `Reflect.construct()` 或 `extends`。

示例
--

### 用 apply() 将数组各项添加到另一个数组

你可以使用 `Array.prototype.push()` 方法将元素追加到数组中。因为 `push()` 接受可变数量的参数，所以你也可以一次性添加多个元素。但是，如果你将一个数组传递给 `push()`，它实际上会将该数组作为单个元素添加，而不是逐个添加元素，导致最终得到一个数组内嵌的数组。另一方面，`Array.prototype.concat()` 在这种情况下具有期望的行为，但它不会将元素追加到*已有*数组中，而是创建并返回一个新数组。

在这种情况下，你可以使用 `apply` 隐式地将一个数组作为一系列参数展开。

```js
const array = ["a", "b"];
const elements = [0, 1, 2];
array.push.apply(array, elements);
console.info(array); // ["a", "b", 0, 1, 2]
```

使用展开语法可以达到相同的效果。

```js
const array = ["a", "b"];
const elements = [0, 1, 2];
array.push(...elements);
console.info(array); // ["a", "b", 0, 1, 2]
```

### 使用 apply() 和内置函数

巧妙地使用 `apply()` 可以让你在某些情况下使用内置函数来完成一些任务，而这些任务通常需要手动遍历集合（或使用展开语法）。

例如，我们可以使用 `Math.max()` 和 `Math.min()` 来找出数组中的最大值和最小值。

```js
// 数组中的最小/最大值
const numbers = [5, 6, 2, 3, 7];

// 用 apply 调用 Math.min/Math.max
let max = Math.max.apply(null, numbers);
// 这等价于 Math.max(numbers[0], …) 或 Math.max(5, 6, …)

let min = Math.min.apply(null, numbers);

// 与基于简单循环的算法相比
max = -Infinity;
min = +Infinity;

for (let i = 0; i < numbers.length; i++) {
  if (numbers[i] > max) {
    max = numbers[i];
  }
  if (numbers[i] < min) {
    min = numbers[i];
  }
}
```

但要注意：通过使用 `apply()`（或展开语法）来处理任意长的参数列表，你可能会超过 JavaScript 引擎的参数长度限制。

调用具有太多参数的函数（即超过数万个参数）的后果是未指定的，并且在不同的引擎中会有所不同。（JavaScriptCore 引擎将参数限制硬编码为 65536。）大多数引擎会抛出异常；但并没有规范要求阻止其他行为，例如任意限制应用函数实际接收的参数数量。为了说明后一种情况：假设这样的引擎限制为四个参数（实际限制当然要高得多），那么在上面的示例中，传递给 `apply` 的参数将变为 `5, 6, 2, 3`，而不是完整的数组。

如果你的值数组可能会增长到数万个，可以使用混合策略：将数组的片段分批通过 `apply` 调用函数：

```js
function minOfArray(arr) {
  let min = Infinity;
  const QUANTUM = 32768;

  for (let i = 0; i < arr.length; i += QUANTUM) {
    const submin = Math.min.apply(
      null,
      arr.slice(i, Math.min(i + QUANTUM, arr.length)),
    );
    min = Math.min(submin, min);
  }

  return min;
}

const min = minOfArray([5, 6, 2, 3, 7]);
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-function.prototype.apply |

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

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

参见
--

* `arguments`
* `Function.prototype.bind()`
* `Function.prototype.call()`
* `Reflect.apply()`
* 函数
* 展开语法


Function.prototype.bind() - JavaScript

Function.prototype.bind()
=========================

`Function` 实例的 **`bind()`** 方法创建一个新函数，当调用该新函数时，它会调用原始函数并将其 `this` 关键字设置为给定的值，同时，还可以传入一系列指定的参数，这些参数会插入到调用新函数时传入的参数的前面。

尝试一下
----

语法
--

```js
bind(thisArg)
bind(thisArg, arg1)
bind(thisArg, arg1, arg2)
bind(thisArg, arg1, arg2, /\* …, \*/ argN)
```

### 参数

`thisArg`

在调用绑定函数时，作为 `this` 参数传入目标函数 `func` 的值。如果函数不在严格模式下，`null` 和 `undefined` 会被替换为全局对象，并且原始值会被转换为对象。如果使用 `new` 运算符构造绑定函数，则忽略该值。

`arg1, …, argN` 可选

在调用 `func` 时，插入到传入绑定函数的参数前的参数。

### 返回值

使用指定的 `this` 值和初始参数（如果提供）创建的给定函数的副本。

描述
--

`bind()` 函数创建一个新的*绑定函数（bound function）*。调用绑定函数通常会执行其所包装的函数，也称为*目标函数（target function）*。绑定函数将绑定时传入的参数（包括 `this` 的值和前几个参数）提前存储为其内部状态。而不是在实际调用时传入。通常情况下，你可以将 `const boundFn = fn.bind(thisArg, arg1, arg2)` 和 `const boundFn = (...restArgs) => fn.call(thisArg, arg1, arg2, ...restArgs)` 构建的绑定函数的调用效果视为等效（但就构建 `boundFn` 的过程而言，不是二者等效的）。

绑定函数可以通过调用 `boundFn.bind(thisArg, /* more args */)` 进一步进行绑定，从而创建另一个绑定函数 `boundFn2`。新绑定的 `thisArg` 值会被忽略，因为 `boundFn2` 的目标函数是 `boundFn`，而 `boundFn` 已经有一个绑定的 `this` 值了。当调用 `boundFn2` 时，它会调用 `boundFn`，而 `boundFn` 又会调用 `fn`。`fn` 最终接收到的参数按顺序为：`boundFn` 绑定的参数、`boundFn2` 绑定的参数，以及 `boundFn2` 接收到的参数。

```js
"use strict"; // 防止 `this` 被封装到到包装对象中

function log(...args) {
  console.log(this, ...args);
}
const boundLog = log.bind("this value", 1, 2);
const boundLog2 = boundLog.bind("new this value", 3, 4);
boundLog2(5, 6); // "this value", 1, 2, 3, 4, 5, 6
```

如果目标函数是可构造的，绑定函数也可以使用 `new` 运算符进行构造。这样做的效果就好像目标函数本身被构造一样。前置的参数会像通常一样传递给目标函数，而提供的 `this` 值会被忽略（因为构造函数会准备自己的 `this`，如 `Reflect.construct` 的参数所示）。如果直接构造绑定函数，`new.target` 将指向目标函数（也就是说，绑定函数对 `new.target` 是透明的）。

```js
class Base {
  constructor(...args) {
    console.log(new.target === Base);
    console.log(args);
  }
}

const BoundBase = Base.bind(null, 1, 2);

new BoundBase(3, 4); // true, [1, 2, 3, 4]
```

然而，由于绑定函数没有 `prototype` 属性，它不能作为 `extends` 的基类。

```js
class Derived extends class {}.bind(null) {}
// TypeError: Class extends value does not have valid prototype property undefined
```

当将绑定函数用作 `instanceof` 运算符右操作数时，`instanceof` 会访问绑定函数内部存储的目标函数，并读取其 `prototype` 属性。

```js
class Base {}
const BoundBase = Base.bind(null, 1, 2);
console.log(new Base() instanceof BoundBase); // true
```

绑定函数具有以下属性：

`length`

目标函数的 `length` 减去被绑定的参数个数（不包括 `thisArg` 参数），最小值为 0。

`name`

目标函数的 `name` 前加上 `"bound "` 前缀。

绑定函数还会继承目标函数的原型链。然而，它不会继承目标函数的其他自有属性（例如，如果目标函数是一个类，则不会继承其静态属性）。

示例
--

### 创建绑定函数

`bind()` 最简单的用法是创建一个函数，无论如何调用，它都会使用特定的 `this` 值进行调用。

JavaScript 新手经常犯的一个错误是将一个方法从对象中提取出来，然后再调用，并期望方法中的 `this` 是原来的对象（比如在回调中传入这个方法）。

然而，如果不做特殊处理的话，通常会丢失原始对象。使用这个函数加上原始的对象来创建一个绑定函数，巧妙地解决了这个问题：

```js
// 顶级的“this”绑定到“globalThis”。
this.x = 9;
const module = {
  x: 81,
  getX() {
    return this.x;
  },
};

// “getX”的“this”参数绑定到“module”。
console.log(module.getX()); // 81

const retrieveX = module.getX;
// “retrieveX”的“this”参数在非严格模式下绑定到“globalThis”。
console.log(retrieveX()); // 9

// 创建一个新函数“boundGetX”，并将“this”参数绑定到“module”。
const boundGetX = retrieveX.bind(module);
console.log(boundGetX()); // 81
```

**备注：** 如果在严格模式下运行这个示例，`retrieveX` 的 `this` 参数会绑定到 `undefined` 而不是 `globalThis`，这会导致 `retrieveX()` 的调用失败。

如果在一个 ECMAScript 模块中运行这个示例，顶级的 `this` 会绑定到 `undefined` 而不是 `globalThis`，导致 `this.x = 9` 赋值失败。

如果在 Node CommonJS 模块中运行这个示例，则顶级的 `this` 会绑定到 `module.exports` 而不是 `globalThis`。然而，在非严格模式下，`retrieveX` 的 `this` 仍然会绑定到 `globalThis`，而在严格模式下会绑定为 `undefined`。因此，在非严格模式下（默认情况下），`retrieveX()` 调用会返回 `undefined`, 因为 `this.x = 9` 会写入的是一个不同的对象（`module.exports`），而 `getX` 读取的是另一个对象（`globalThis`）。

实际上，一些内置的“方法”也是返回绑定函数的 getter 方法，其中一个显著的例子是 `Intl.NumberFormat.prototype.format()`，当访问时，它返回一个绑定函数，你可以直接将其作为回调函数传递。

### 偏函数

`bind()` 的另一个简单用法是创建一个具有预设初始参数的函数。

这些参数（如果有的话）会跟随提供的 `this` 值，并在传递给目标函数的参数列表的开头插入，其后是在调用绑定函数时传递的参数。

```js
function list(...args) {
  return args;
}

function addArguments(arg1, arg2) {
  return arg1 + arg2;
}

console.log(list(1, 2, 3)); // [1, 2, 3]

console.log(addArguments(1, 2)); // 3

// 创建一个带有预设前导参数的函数
const leadingThirtySevenList = list.bind(null, 37);

// 创建一个带有预设第一个参数的函数。
const addThirtySeven = addArguments.bind(null, 37);

console.log(leadingThirtySevenList()); // [37]
console.log(leadingThirtySevenList(1, 2, 3)); // [37, 1, 2, 3]
console.log(addThirtySeven(5)); // 42
console.log(addThirtySeven(5, 10)); // 42
//（最后一个参数 10 被忽略）
```

### 配合 setTimeout()

在默认情况下，在 `setTimeout()` 内部，`this` 关键字将被设置为 `globalThis`，在浏览器中它是 `window` 对象。当处理需要将 `this` 引用类实例的类方法时，你可以显式地将 `this` 绑定到回调函数，以便保持实例的引用。

```js
class LateBloomer {
  constructor() {
    this.petalCount = Math.floor(Math.random() \* 12) + 1;
  }
  bloom() {
    // 延迟 1 秒后宣布开花
    setTimeout(this.declare.bind(this), 1000);
  }
  declare() {
    console.log(`I am a beautiful flower with ${this.petalCount} petals!`);
  }
}

const flower = new LateBloomer();
flower.bloom();
// 1 秒后调用“flower.declare()”
```

你还可以使用箭头函数来实现此目的。

```js
class LateBloomer {
  bloom() {
    // 延迟 1 秒后宣布开花
    setTimeout(() => this.declare(), 1000);
  }
}
```

### 作为构造函数使用的绑定函数

绑定函数自动适用于与 `new` 运算符一起使用，以用于构造目标函数创建的新实例。当使用绑定函数是用来构造一个值时，提供的 `this` 会被忽略。然而，提供的参数仍会被插入到构造函数调用时的参数列表之前。

```js
function Point(x, y) {
  this.x = x;
  this.y = y;
}

Point.prototype.toString = function () {
  return `${this.x},${this.y}`;
};

const p = new Point(1, 2);
p.toString();
// '1,2'

// thisArg 的值并不重要，因为它被忽略了
const YAxisPoint = Point.bind(null, 0 /\*x\*/);

const axisPoint = new YAxisPoint(5);
axisPoint.toString(); // '0,5'

axisPoint instanceof Point; // true
axisPoint instanceof YAxisPoint; // true
new YAxisPoint(17, 42) instanceof Point; // true
```

请注意，你无需采取任何特殊措施来创建一个绑定函数，以便与 `new` 运算符一起使用。`new.target`、`instanceof`、`this` 等都如预期工作，就好像构造函数从未被绑定一样。唯一的区别是它不能再用于 `extends`。

相应地，你无需采取任何特殊措施来创建一个绑定函数，使其只能以普通方式调用，即使你更希望要求只能使用 `new` 来调用绑定函数。如果你在没有使用 `new` 的情况下调用它，绑定的 `this` 将不再被忽略。

```js
const emptyObj = {};
const YAxisPoint = Point.bind(emptyObj, 0 /\*x\*/);

// 仍然可以作为普通函数调用
//（尽管通常这是不可取的）
YAxisPoint(13);

// 现在可以从外部观察对 `this` 的修改
console.log(emptyObj); // { x: 0, y: 13 }
```

如果你希望限制绑定函数只能使用 `new` 调用，或者只能在没有使用 `new` 的情况下调用，目标函数必须强制执行该限制，例如通过检查 `new.target !== undefined` 或使用 class。

### 绑定类

在类上使用 `bind()` 会保留大部分类的语义，只是当前类的所有静态自有属性会丢失。然而，由于原型链被保留，你仍然可以访问从父类继承的静态属性。这意味着绑定后的类实例仍然可以享受到继承自父类的静态属性的功能。

```js
class Base {
  static baseProp = "基类属性";
}

class Derived extends Base {
  static derivedProp = "派生类属性";
}

const BoundDerived = Derived.bind(null);
console.log(BoundDerived.baseProp); // "基类属性"
console.log(BoundDerived.derivedProp); // undefined
console.log(new BoundDerived() instanceof Derived); // true
```

### 将方法转换为实用函数

`bind()` 在某些情况下也非常有用，比如当你想将一个需要特定 `this` 值的方法转换为一个普通的实用函数，该函数将之前的 `this` 参数作为普通参数传入。这类似于通用实用函数的工作方式：而不是调用 `array.map(callback)`，你可以使用 `map(array, callback)`，这样可以避免修改 `Array.prototype`，并且可以在不是数组的类数组对象（比如 `arguments`）上使用 `map`。

以 `Array.prototype.slice()` 为例，你可以使用它将类数组对象转换为真正的数组。你可以创建一个类似下面的快捷方式：

```js
const slice = Array.prototype.slice;

// ...

slice.call(arguments);
```

请注意，你不能保存 `slice.call` 并将其作为普通函数调用，因为 `call()` 方法还会读取其应该调用的函数作为其 `this` 值。在这种情况下，你可以使用 `bind()` 来绑定 `call()` 的 `this` 值。在下面的代码片段中，`slice()` 是一个绑定了 `this` 值为 `Array.prototype.slice()` 的 `Function.prototype.call()` 的版本。这意味着可以消除额外的 `call()` 调用：

```js
// 与上一个示例中的“slice”相同
const unboundSlice = Array.prototype.slice;
const slice = Function.prototype.call.bind(unboundSlice);

// ...

slice(arguments);
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-function.prototype.bind |

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

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

参见
--

* `core-js` 中 `Function.prototype.bind` 的 polyfill
* `Function.prototype.apply()`
* `Function.prototype.call()`
* 函数


Function.prototype[@@hasInstance]() - JavaScript

Function.prototype[@@hasInstance]()
===================================

`Function` 实例的 **`[@@hasInstance]()`** 方法指定确定构造函数是否将对象识别为其实例的默认过程。它由 `instanceof` 运算符调用。

语法
--

```js
func[Symbol.hasInstance](value)
```

### 参数

`value`

要测试的对象，原始值始终返回 `false`。

### 返回值

如果 `func.prototype` 在 `value` 的原型链中，则返回 `true`，否则返回 `fals`；如果 `value` 不是一个对象或 `this` 不是一个函数，则始终返回 `false`。如果 `this` 是一个绑定函数，则返回对 `value` 和底层目标函数进行 `instanceof` 测试的结果。

### 异常

`TypeError`

如果 `this` 不是绑定函数且 `this.prototype` 不是对象，则会抛出此异常。

描述
--

`instanceof` 运算符在右操作数存在 `@@hasInstance()` 方法时会调用该方法。由于所有函数默认继承自 `Function.prototype`，它们都会具有 `[@@hasInstance]()` 方法，因此大多数情况下，`Function.prototype[@@hasInstance]` 方法指定了右操作数为函数时 `instanceof` 的行为。该方法实现了 `instanceof` 运算符的默认行为（当 `constructor` 没有 `@@hasInstance` 方法时使用相同算法）。

与大多数方法不同，`Function.prototype[@@hasInstance]()` 属性是不可配置且不可写的。这是一个安全特性，用于防止绑定函数的底层目标函数被获取。请参考这个 StackOverflow 回答中的示例。

示例
--

### 恢复默认的 instanceof 行为

你很少需要直接调用这个方法。相反，这个方法是由 `instanceof` 运算符调用的。通常情况下，你应该期望这两种结果是等价的。

```js
class Foo {}
const foo = new Foo();
console.log(foo instanceof Foo === Foo[Symbol.hasInstance](foo)); // true
```

如果你想调用默认的 `instanceof` 行为，但不确定构造函数是否已经重写了 `[@@hasInstance]()` 方法，那么你可以使用这个方法。

```js
class Foo {
  static [Symbol.hasInstance](value) {
    // 自定义实现
    return false;
  }
}

const foo = new Foo();
console.log(foo instanceof Foo); // false
console.log(Function.prototype[Symbol.hasInstance].call(Foo, foo)); // true
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-function.prototype-@@hasinstance |

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

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

参见
--

* `instanceof`
* `Symbol.hasInstance`


Function.prototype.prototype - JavaScript

Function.prototype.prototype
============================

当 `Function` 实例作为构造函数与 `new` 运算符一起使用时，该实例的 **`prototype`** 数据属性将用作新对象的原型。

**备注：** 并不是所有的 `Function` 对象都拥有 `prototype` 属性——参见描述。

值
-

一个对象。

| `Function.prototype.prototype` 的属性特性 |
| --- |
| 可写 | 是 |
| 可枚举 | 否 |
| 可配置 | 否 |

**备注：** 类的 `prototype` 属性是不可写的。

描述
--

在使用 `new` 运算符调用函数时，构造函数的 `prototype` 属性将成为新对象的原型。

```js
function Ctor() {}
const inst = new Ctor();
console.log(Object.getPrototypeOf(inst) === Ctor.prototype); // true
```

关于构造函数的 `prototype` 属性与结果对象的原型之间的相互作用，你可以查看继承与原型链来了解更多。

一个具有 `prototype` 属性的函数也并不代表其有资格作为构造函数。例如，function\* 拥有 `prototype` 属性，但它不能通过 `new` 运算符来调用。

```js
async function\* asyncGeneratorFunction() {}
function\* generatorFunction() {}
```

反之，生成器函数通常在它们不被 `new` 运算符调用的时候被用到它们的 `prototype` 属性。 `prototype` 属性会作为返回的 `Generator` 对象的原型。

另外，一些函数也可能在通过 `new` 运算符调用的情况下无条件抛出，即便它们可能具有 `prototype`。例如，`Symbol()` 和 `BigInt()` 函数会在它们通过 `new` 运算符来调用时抛出，因为 `Symbol.prototype` and `BigInt.prototype` 只是用来为原始值提供方法的，这时不应该直接构建包装器对象。

下列的函数不具有 `prototype` 属性，因此不能成为构造函数，即便后续手动赋予了 `prototype` 属性：

```js
const method = { foo() {} }.foo;
const arrowFunction = () => {};
async function asyncFunction() {}
```

下列则可以成为合法的构造函数，因为它们具有 `prototype`：

```js
class Class {}
function fn() {}
```

绑定函数不具有 `prototype` 属性，但是可能是可构造的。当它被构造的时候，目标函数将会被构造，如果目标函数是可构造的，将会返回一个普通的实例。

```js
const boundFunction = function () {}.bind(null);
```

默认情况下，函数的 `prototype` 是一个普通的对象。这个对象具有一个属性：`constructor`。它是对这个函数本身的一个引用。 `constructor` 属性是可编辑、可配置但不可枚举的。

如果函数的 `prototype` 被赋予了 `Object` 以外的值，则当它被 `new` 运算符调用时，返回对象的原型将会指向 `Object.prototype`。（换句话说，`new` 运算符会忽略它的 `prototype` 属性并构造一个普通对象。）

```js
function Ctor() {}
Ctor.prototype = 3;
console.log(Object.getPrototypeOf(new Ctor()) === Object.prototype); // true
```

示例
--

### 通过修改原型的属性来修改所有实例的原型

```js
function Ctor() {}
const p1 = new Ctor();
const p2 = new Ctor();
Ctor.prototype.prop = 1;
console.log(p1.prop); // 1
console.log(p2.prop); // 1
```

### 将非函数属性添加到类的原型中

公有类字段会为每一个实例都添加属性。类方法可以在原型中定义*函数*属性。但是，无法将非函数属性添加到原型中。假如你希望在所有实例之间共享静态数据（例如，`Error.prototype.name` 在所有 Error 类的实例中都是相同的），那么你可以在类的原型中进行手动添加。

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

Dog.prototype.species = "dog";

console.log(new Dog("Jack").species); // "dog"
```

使用静态初始化块 (en-US)可以让这个操作更符合直觉。这些块会在类初始化的时候进行调用。

```js
class Dog {
  static {
    Dog.prototype.species = "dog";
  }
  constructor(name) {
    this.name = name;
  }
}

console.log(new Dog("Jack").species); // "dog"
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-function-instances-prototype |

参见
--

* `Function`
* 继承与原型链


Function：length - JavaScript

Function：length
===============

`Function` 实例的 **`length`** 数据属性表示函数期望的参数数量。

尝试一下
----

值
-

一个数字。

| `Function：length` 的属性特性 |
| --- |
| 可写 | 否 |
| 可枚举 | 否 |
| 可配置 | 是 |

描述
--

一个 `Function` 对象的 `length` 属性表示函数期望的参数个数，即形参的个数。这个数字不包括剩余参数，只包括在第一个具有默认值的参数之前的参数。相比之下，`arguments.length` 是局限于函数内部的，它提供了实际传递给函数的参数个数。

`Function` 构造函数本身就是一个 `Function` 对象。它的 `length` 数据属性的值为 `1`。

由于历史原因，`Function.prototype` 本身是可调用的。`Function.prototype` 的 `length` 属性的值为 `0`。

示例
--

### 使用 function length

```js
console.log(Function.length); // 1

console.log((() => {}).length); // 0
console.log(((a) => {}).length); // 1
console.log(((a, b) => {}).length); // 2，依此类推

console.log(((...args) => {}).length);
// 0，剩余参数不计算在内

console.log(((a, b = 1, c) => {}).length);
// 1，只计算第一个具有默认值的参数之前的参数
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-function-instances-length |

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

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

参见
--

* `Function`


Function.prototype.caller - JavaScript

Function.prototype.caller
=========================

**非标准:** 该特性是非标准的，请尽量不要在生产环境中使用它！

**已弃用:** 不再推荐使用该特性。虽然一些浏览器仍然支持它，但也许已从相关的 web 标准中移除，也许正准备移除或出于兼容性而保留。请尽量不要使用该特性，并更新现有的代码；参见本页面底部的兼容性表格以指导你作出决定。请注意，该特性随时可能无法正常工作。

**备注：** 在严格模式下，访问函数的 `caller` 属性会抛出错误——该 API 已被移除且没有替代品。这是为了防止代码能够“遍历堆栈”，这既存在安全风险，也严重限制了内联和尾调用优化等优化的可能性。如需更多解释，请阅读 arguments.callee 的弃用原因。

`Function` 实例的 **`caller`** 访问器属性返回调用该函数的函数。对于严格模式、箭头函数、异步函数和生成器函数来说，访问 `caller` 属性会抛出 `TypeError`。

描述
--

如果函数 `f` 是在全局作用域内调用的，则 `f.caller` 的值为 `null`；否则它就是调用 `f` 的函数。如果调用 `f` 的函数是一个严格模式函数，则 `f.caller` 的值也是 `null`。

请注意，ECMAScript 规范规定的唯一行为是 `Function.prototype` 具有一个初始的 `caller` 访问器，无论是 `get` 还是 `set` 请求，它都会无条件地抛出 `TypeError`（称为“毒丸访问器”）。而且引擎实现不允许改变此语义，除非是非严格的普通函数。在这种情况下，它不能具有严格模式函数的值。`caller` 属性的实际行为如果不是抛出错误，则该行为是由实现定义的。例如，Chrome 将其定义为自有数据属性，而 Firefox 和 Safari 扩展了初始的毒丸访问器 `Function.prototype.caller`，以特殊处理非严格模式的函数的 `this` 值。

```js
(function f() {
  if (Object.hasOwn(f, "caller")) {
    console.log(
      "caller 是一个自有属性，具有描述符",
      Object.getOwnPropertyDescriptor(f, "caller"),
    );
  } else {
    console.log(
      "f 没有名为 caller 的自有属性。尝试获取 f.[[Prototype]].caller",
    );
    console.log(
      Object.getOwnPropertyDescriptor(
        Object.getPrototypeOf(f),
        "caller",
      ).get.call(f),
    );
  }
})();

// 在 Chrome 中：
// caller 是一个自有属性，具有描述符 {value: null, writable: false, enumerable: false, configurable: false}

// 在 Firefox 中：
// f 没有名为 caller 的自有属性。尝试获取 f.[[Prototype]].caller
// null
```

此属性取代了 `arguments` 对象的已弃用的 `arguments.caller` 属性。

出于安全原因，特殊属性 `__caller__` 已被移除，它返回调用者的激活对象，从而允许重建堆栈。

示例
--

### 检查函数 caller 属性的值

以下代码检查函数的 `caller` 属性的值。

```js
function myFunc() {
  if (myFunc.caller === null) {
    return "该函数是从顶层调用的！";
  } else {
    return `该函数的调用者是 ${myFunc.caller}`;
  }
}
```

### 重建调用堆栈和递归

请注意，在递归的情况下，你不能使用该属性重建调用堆栈。参考以下示例：

```js
function f(n) {
  g(n - 1);
}
function g(n) {
  if (n > 0) {
    f(n);
  } else {
    stop();
  }
}
f(2);
```

当调用 `stop()` 时，调用堆栈将是：
```

f(2) -> g(1) -> f(1) -> g(0) -> stop()
```

以下是条件表达式为真：

```js
stop.caller === g && f.caller === g && g.caller === f;
```

因此，如果你尝试像这样在 `stop()` 函数中获取堆栈跟踪：

```js
let f = stop;
let stack = "Stack trace:";
while (f) {
  stack += `\n${f.name}`;
  f = f.caller;
}
```

循环永远不会停止。

### 严格模式下的 caller

如果调用者是严格模式函数，则 `caller` 的值为 `null`。

```js
function callerFunc() {
  calleeFunc();
}

function strictCallerFunc() {
  "use strict";
  calleeFunc();
}

function calleeFunc() {
  console.log(calleeFunc.caller);
}

(function () {
  callerFunc();
})();
// 输出 [Function: callerFunc]

(function () {
  strictCallerFunc();
})();
// 输出 null
```

规范
--

不属于任何标准。

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

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

参见
--

* `Function.prototype.name`
* `arguments`


Symbol.unscopables - JavaScript

Symbol.unscopables
==================

**`Symbol.unscopables`** 指用于指定对象值，其对象自身和继承的从关联对象的 with 环境绑定中排除的属性名称。

尝试一下
----

描述
--

可以在任何对象上定义 `@@unscopables` symbol (`Symbol.unscopables`)，用于排除属性名称并与 `with` 环境绑定在一起作为词法变量公开。请注意，如果使用 Strict mode，语句将不可用，并且可能也不需要 symbol。

在 `unscopables` 对象上设置属性为 true，将使其 *unscopable* 并且因此该属性也将不会在词法环境变量中出现。如果设置属性为 `false` ，则将使其可 `scopable` 并且该属性会出现在词法环境变量中。

| `Symbol.unscopables` 的属性特性 |
| --- |
| 可写 | 否 |
| 可枚举 | 否 |
| 可配置 | 否 |

示例
--

下列的代码可兼容 ES5 及以下版本。然而，在 ECMAScript 2015 (ES6) 或其后续版本中，`Array.prototype.keys()` 方法才会出现。意味着内部 `with` 环境“关键字”存在该方法，但变量中不会存在。也就是说，当 `unscopable`s symbol 被展示时，内置的`unscopables` 设置是由 `Array.prototype[@@unscopables]` 展示并实现的，一些 Array 的方法 将作为 scoped 放入 `with` 语句中。

```js
var keys = [];

with (Array.prototype) {
  keys.push("something");
}

Object.keys(Array.prototype[Symbol.unscopables]);
// ["copyWithin", "entries", "fill", "find", "findIndex",
// "includes", "keys", "values"]
```

你也可以为你自己的对象设置 `unscopables` 。

```js
var obj = {
  foo: 1,
  bar: 2,
};

obj[Symbol.unscopables] = {
  foo: false,
  bar: true,
};

with (obj) {
  console.log(foo); // 1
  console.log(bar); // ReferenceError: bar is not defined
}
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-symbol.unscopables |

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

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

参见
--

* `Array.prototype[@@unscopables]`
* `with` 语句（在严格模式中不可用）


Symbol.toPrimitive - JavaScript

Symbol.toPrimitive
==================

**`Symbol.toPrimitive`** 是内置的 symbol 属性，其指定了一种接受首选类型并返回对象原始值的表示的方法。它被所有的强类型转换制算法优先调用。

尝试一下
----

| `Symbol.toPrimitive` 的属性特性 |
| --- |
| 可写 | 否 |
| 可枚举 | 否 |
| 可配置 | 否 |

描述
--

在 `Symbol.toPrimitive` 属性（用作函数值）的帮助下，对象可以转换为一个原始值。该函数被调用时，会被传递一个字符串参数 `hint`，表示要转换到的原始值的预期类型。`hint` 参数的取值是 `"number"`、`"string"` 和 `"default"` 中的任意一个。

`"number"` hint 用于强制数字类型转换算法。`"string"` hint 用于强制字符串类型转换算法。`"default"` hint 用于强制原始值转换算法。`hint` 仅是作为首选项的偏弱的信号提示，实现时，可以自由忽略它（就像 `Symbol.prototype[@@toPrimitive]()` 一样）。该语言不会在 `hint` 和结果类型之间强制校正，尽管 `[@@toPrimitive]()` 必须返回一个原始值，否则将抛出 `TypeError`。

没有 `@@toPrimitive` 属性的对象通过以不同的顺序调用 `valueOf()` 和 `toString()` 方法将其转换为原始值，这在强制类型转换部分进行了更详细的解释。`@@toPrimitive` 允许完全控制原始转换过程。例如，`Date.prototype[@@toPrimitive]` 将 `"default"` 视为 `"string"` 并且调用 `toString()` 而不是 `valueOf()`。`Symbol.prototype[@@toPrimitive]` 忽略 hint，并总是返回一个 symbol，这意味着即使在字符串上下文中，也不会调用 `Symbol.prototype.toString()`，并且 `Symbol` 对象必须始终通过 `String()` 显式转换为字符串。

示例
--

### 修改从对象转换的原始值

以下示例描述了 `Symbol.toPrimitive` 属性如何修改从对象转换的原始值。

```js
// 一个没有提供 Symbol.toPrimitive 属性的对象，参与运算时的输出结果。
const obj1 = {};
console.log(+obj1); // NaN
console.log(`${obj1}`); // "[object Object]"
console.log(obj1 + ""); // "[object Object]"

// 接下面声明一个对象，手动赋予了 Symbol.toPrimitive 属性，再来查看输出结果。
const obj2 = {
  [Symbol.toPrimitive](hint) {
    if (hint === "number") {
      return 10;
    }
    if (hint === "string") {
      return "hello";
    }
    return true;
  },
};
console.log(+obj2); // 10 — hint 参数值是 "number"
console.log(`${obj2}`); // "hello" — hint 参数值是 "string"
console.log(obj2 + ""); // "true" — hint 参数值是 "default"
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-symbol.toprimitive |

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

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

参见
--

* `Date.prototype[@@toPrimitive]()`
* `Symbol.prototype[@@toPrimitive]()`
* `Object.prototype.toString()`
* `Object.prototype.valueOf()`


Symbol() 构造函数 - JavaScript

Symbol() 构造函数
=============

**`Symbol()`** 构造函数返回一个 **symbol** 类型的值，但是它并不完全支持构造函数的语法，因为它不支持 `new Symbol()` 语法，也无法被子类化。它可以作为 `class` 定义中 `extends` 子句的值使用，但对它进行 `super` 调用将会导致异常。

尝试一下
----

语法
--

```js
Symbol()
Symbol(description)
```

**备注：** `Symbol()` 只能在没有 `new` 的情况下调用，尝试用 `new` 构造会抛出 `TypeError`。

参数
--

`description` 可选

一个字符串，用来表示 symbol 的描述，可以用于调试，但不能直接访问 symbol 自身。

示例
--

### 创建 symbol

要创建一个新的原始 symbol，你可以传递一个可选的字符串作为 `description` 参数来创建 `Symbol()`：

```js
const sym1 = Symbol();
const sym2 = Symbol("foo");
const sym3 = Symbol("foo");
```

上面的代码创建了三个新的 symbol，注意 `Symbol("foo")` 不会将字符串 `"foo"` 强制转换成 symbol，而是每次总是创建一个新的 symbol：

```js
Symbol("foo") === Symbol("foo"); // false
```

### new Symbol()

下面带有 `new` 运算符的语法将会抛出一个 `TypeError`：

```js
const sym = new Symbol(); // TypeError
```

这一限制是为了防止开发人员创建一个显式的 `Symbol` 包装对象，而不是一个新的 symbol 值。这可能会让人感到困扰，因为在通常情况下，可以创建基本数据类型的显式包装对象（例如 `new Boolean`、`new String` 和 `new Number`）。

如果你真的想创建一个 `Symbol` 包装器对象，你可以使用 `Object()` 函数：

```js
const sym = Symbol("foo");
const symObj = Object(sym);
typeof sym; // "symbol"
typeof symObj; // "object"
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-symbol-constructor |

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

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

参见
--

* `core-js` 中 `Symbol` 的 Polyfill


Symbol.species - JavaScript

Symbol.species
==============

知名的 **`Symbol.species`** 是个函数值属性，其被构造函数用以创建派生对象。

尝试一下
----

| `Symbol.species` 的属性特性 |
| --- |
| 可写 | 否 |
| 可枚举 | 否 |
| 可配置 | 否 |

描述
--

species 访问器属性允许子类覆盖对象的默认构造函数。

示例
--

你可能想在扩展数组类 `MyArray` 上返回 `Array` 对象。例如，当使用例如 `map()` 这样的方法返回默认的构造函数时，你希望这些方法能够返回父级的 Array 对象，以取代 `MyArray` 对象。`Symbol.species` 允许你这么做：

```js
class MyArray extends Array {
  // 覆盖 species 到父级的 Array 构造函数上
  static get [Symbol.species]() {
    return Array;
  }
}
var a = new MyArray(1, 2, 3);
var mapped = a.map((x) => x \* x);

console.log(mapped instanceof MyArray); // false
console.log(mapped instanceof Array); // true
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-symbol.species |

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

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

参见
--

* `Map[@@species]`
* `Set[@@species]`


Symbol.split - JavaScript

Symbol.split
============

**`Symbol.split`** 指向 一个正则表达式的索引处分割字符串的方法。这个方法通过 `String.prototype.split()` 调用。

详情请参阅`RegExp.prototype[@@split]()` 和`String.prototype.split()`.

尝试一下
----

| `Symbol.split` 的属性特性 |
| --- |
| 可写 | 否 |
| 可枚举 | 否 |
| 可配置 | 否 |

示例
--

[Symbol.split] 指向‘aba’.split(/a/)

```js
/a/[Symbol.split]("aba", 3);
```

"dayinlove".split(exp) 调用 [Symbol.split](str) 处理，并把实参"dayinlove"传给形参 str

```js
var exp = {
  pat: "in",
  [Symbol.split](str) {
    return str.split(this.pat);
  },
};

"dayinlove".split(exp);
//["day", "love"]
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-symbol.split |

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

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

参阅
--

* `Symbol.match`
* `Symbol.replace`
* `Symbol.search`
* `RegExp.prototype[@@split]()`


Symbol.prototype.description - JavaScript

Symbol.prototype.description
============================

**`description`** 是一个只读属性，它会返回 `Symbol` 对象的可选描述的字符串。

尝试一下
----

语法
--
```

Symbol('myDescription').description;
Symbol.iterator.description;
Symbol.for('foo').description;
```

描述
--

`Symbol` 对象可以通过一个可选的描述创建，可用于调试，但不能用于访问 symbol 本身。`Symbol.prototype.description` 属性可以用于读取该描述。与 `Symbol.prototype.toString()` 不同的是它不会包含 "`Symbol()`" 的字符串。具体请看实例。

示例
--

```js
Symbol("desc").toString(); // "Symbol(desc)"
Symbol("desc").description; // "desc"
Symbol("").description; // ""
Symbol().description; // undefined

// 内置通用（well-known）symbol
Symbol.iterator.toString(); // "Symbol(Symbol.iterator)"
Symbol.iterator.description; // "Symbol.iterator"

// global symbols
Symbol.for("foo").toString(); // "Symbol(foo)"
Symbol.for("foo").description; // "foo"
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-symbol.prototype.description |

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

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

参见
--

* `Symbol.prototype.toString()`
* Polyfill: https://npmjs.com/symbol.prototype.description


Symbol.match - JavaScript

Symbol.match
============

**`Symbol.match`** 指定了匹配的是正则表达式而不是字符串。`String.prototype.match()` 方法会调用此函数。

尝试一下
----

描述
--

此函数还用于标识对象是否具有正则表达式的行为。比如， `String.prototype.startsWith()`，`String.prototype.endsWith()` 和 `String.prototype.includes()` 这些方法会检查其第一个参数是否是正则表达式，是正则表达式就抛出一个`TypeError`。现在，如果 `match` symbol 设置为 `false`（或者一个 假值），就表示该对象不打算用作正则表达式对象。

| `Symbol.match` 的属性特性 |
| --- |
| 可写 | 否 |
| 可枚举 | 否 |
| 可配置 | 否 |

示例
--

### 禁止表达式检查

下面代码会抛出一个 `TypeError`：

```js
"/bar/".startsWith(/bar/);

// Throws TypeError，因为 /bar/ 是一个正则表达式
// 且 Symbol.match 没有修改。
```

但是，如果你将 `Symbol.match` 置为 `false`，使用 `match` 属性的表达式检查会认为该对象不是正则表达式对象。`startsWith` 和 `endsWith` 方法将不会抛出 `TypeError`。

```js
var re = /foo/;
re[Symbol.match] = false;
"/foo/".startsWith(re); // true
"/baz/".endsWith(re); // false
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-symbol.match |

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

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

参见
--

* `Symbol.replace`
* `Symbol.search`
* `Symbol.split`
* `RegExp.prototype[@@match]()`


Symbol.for() - JavaScript

Symbol.for()
============

**`Symbol.for(key)`** 方法会根据给定的键 `key`，来从运行时的 symbol 注册表中找到对应的 symbol，如果找到了，则返回它，否则，新建一个与该键关联的 symbol，并放入全局 symbol 注册表中。

语法
--
```

Symbol.for(key);
```

### 参数

key

一个字符串，作为 symbol 注册表中与某 symbol 关联的键（同时也会作为该 symbol 的描述）。

### 返回值

返回由给定的 key 找到的 symbol，否则就是返回新创建的 symbol。

描述
--

和 `Symbol()` 不同的是，用 `Symbol.for()` 方法创建的的 symbol 会被放入一个全局 symbol 注册表中。`Symbol.for() 并不是每次都会创建一个新的 symbol`，它会首先检查给定的 key 是否已经在注册表中了。假如是，则会直接返回上次存储的那个。否则，它会再新建一个。

### 全局 symbol 注册表

symbol 注册表中的记录结构：

| 字段名 | 字段值 |
| --- | --- |
| [[key]] | 一个字符串，用来标识每个 symbol |
| [[symbol]] | 存储的 symbol 值 |

示例
--

```js
Symbol.for("foo"); // 创建一个 symbol 并放入 symbol 注册表中，键为 "foo"
Symbol.for("foo"); // 从 symbol 注册表中读取键为"foo"的 symbol

Symbol.for("bar") === Symbol.for("bar"); // true，证明了上面说的
Symbol("bar") === Symbol("bar"); // false，Symbol() 函数每次都会返回新的一个 symbol

var sym = Symbol.for("mario");
sym.toString();
// "Symbol(mario)"，mario 既是该 symbol 在 symbol 注册表中的键名，又是该 symbol 自身的描述字符串
```

为了防止冲突，最好给你要放入 symbol 注册表中的 symbol 带上键前缀。

```js
Symbol.for("mdn.foo");
Symbol.for("mdn.bar");
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-symbol.for |

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

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

参见
--

* `Symbol.keyFor()`


Symbol.search - JavaScript

Symbol.search
=============

`Symbol.search` 指定了一个搜索方法，这个方法接受用户输入的正则表达式，返回该正则表达式在字符串中匹配到的下标，这个方法由以下的方法来调用 `String.prototype.search()`。

更多信息请参见 `RegExp.prototype[@@search]()` 和`String.prototype.search()`.

| `Symbol.search` 的属性特性 |
| --- |
| 可写 | 否 |
| 可枚举 | 否 |
| 可配置 | 否 |

案例
--

### 自定义字符串搜索
```

class caseInsensitiveSearch {
  constructor(value) {
    this.value = value.toLowerCase();
  }
  [Symbol.search](string) {
    return string.toLowerCase().indexOf(this.value);
  }
}

console.log('foobar'.search(new caseInsensitiveSearch('BaR')));
// expected output: 3
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-symbol.search |

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

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

参见
--

* `Symbol.match`
* `Symbol.replace`
* `Symbol.split`
* `RegExp.prototype[@@search]()`


Symbol.replace - JavaScript

Symbol.replace
==============

**`Symbol.replace`** 这个属性指定了当一个字符串替换所匹配字符串时所调用的方法。`String.prototype.replace()` 方法会调用此方法。

更多信息，详见 `RegExp.prototype[@@replace]()` 和 `String.prototype.replace()`。

尝试一下
----

| `Symbol.replace` 的属性特性 |
| --- |
| 可写 | 否 |
| 可枚举 | 否 |
| 可配置 | 否 |

Specifications
--------------

| Specification |
| --- |
| ECMAScript Language Specification # sec-symbol.replace |

Browser compatibility
---------------------

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

See also
--------

* `Symbol.match`
* `Symbol.search`
* `Symbol.split`
* `RegExp.prototype[@@replace]()`


Symbol.prototype.valueOf() - JavaScript

Symbol.prototype.valueOf()
==========================

概述
--

**`valueOf()`** 方法返回当前 symbol 对象所包含的 symbol 原始值。

语法
--
```

symbol.valueOf();
```

描述
--

在 JavaScript 中，虽然大多数类型的对象在某些操作下都会自动的隐式调用自身的 `valueOf()` 方法或者 `toString()` 方法来将自己转换成一个原始值，但 symbol 对象不会这么干，symbol 对象无法隐式转换成对应的原始值：

```js
Object(Symbol("foo")) + "bar";
// TypeError: can't convert symbol object to primitive
// 无法隐式的调用 valueOf() 方法

Object(Symbol("foo")).valueOf() + "bar";
// TypeError: can't convert symbol to string
// 手动调用 valueOf() 方法，虽然转换成了原始值，但 symbol 原始值不能转换为字符串

Object(Symbol("foo")).toString() + "bar";
// "Symbol(foo)bar"，需要手动调用 toString() 方法才行
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-symbol.prototype.valueof |

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

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

参见
--

* `Object.prototype.valueOf()`


Symbol.prototype[@@toPrimitive] - JavaScript

Symbol.prototype[@@toPrimitive]
===============================

**`[@@toPrimitive]()`** 方法可将 Symbol 对象转换为原始值。

语法
--

```js
Symbol()[Symbol.toPrimitive](hint)
```

### 返回值

指定的 `Symbol` 对象的原始值。

描述
--

`hint` 参数未使用时，`Symbol` 的 `[@@toPrimitive]()` 方法返回 Symbol 对象的原始值作为数据类型返回。

JavaScript 调用 `[@@toPrimitive]()` 方法将一个对象转换为原始值表示。你不需要自己调用 `[@@toPrimitive]()` 方法；当对象需要被转换为原始值时，JavaScript 会自动地调用该方法。

示例
--

### 使用 @@toPrimitive

```js
const sym = Symbol("example");
sym === sym[Symbol.toPrimitive](); // true
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-symbol.prototype-@@toprimitive |

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

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

参见
--

* `Symbol.toPrimitive`


Symbol.asyncIterator - JavaScript

Symbol.asyncIterator
====================

**`Symbol.asyncIterator`** 符号指定了一个对象的默认异步迭代器。如果一个对象设置了这个属性，它就是异步可迭代对象，可用于`for await...of`循环。

描述
--

`Symbol.asyncIterator` 是一个用于访问对象的`@@asyncIterator`方法的内建符号。一个异步可迭代对象必须要有`Symbol.asyncIterator`属性。

| `Symbol.asyncIterator` 的属性特性 |
| --- |
| 可写 | 否 |
| 可枚举 | 否 |
| 可配置 | 否 |

示例
--

### 自定义异步可迭代对象

你可以通过设置`[Symbol.asyncIterator]`属性来自定义异步可迭代对象。

```js
const myAsyncIterable = new Object();
myAsyncIterable[Symbol.asyncIterator] = async function\* () {
  yield "hello";
  yield "async";
  yield "iteration!";
};

(async () => {
  for await (const x of myAsyncIterable) {
    console.log(x);
    // expected output:
    // "hello"
    // "async"
    // "iteration!"
  }
})();
```

### 内建异步可迭代对象

目前没有默认设定了`[Symbol.asyncIterator]`属性的 JavaScript 内建的对象。不过，WHATWG（网页超文本应用技术工作小组）Streams 会被设定为第一批异步可迭代对象，`[Symbol.asyncIterator]` 最近已在设计规范中落地。

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-symbol.asynciterator |

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

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

参见
--

* Iteration protocols
* for await... of


Symbol.isConcatSpreadable - JavaScript

Symbol.isConcatSpreadable
=========================

内置的 **`Symbol.isConcatSpreadable`** 符号用于配置某对象作为 `Array.prototype.concat()` 方法的参数时是否展开其数组元素。

尝试一下
----

描述
--

`@@isConcatSpreadable` 符号 (`Symbol.isConcatSpreadable`) 可以直接定义为对象属性或继承而来，它是布尔类型。它可以控制数组或类似数组（array-like）的对象的行为：

* 对于数组对象，默认情况下，用于 concat 时，会按数组元素展开然后进行连接（数组元素作为新数组的元素）。重置`Symbol.isConcatSpreadable`可以改变默认行为。
* 对于类似数组的对象，用于 concat 时，该对象整体作为新数组的元素，重置`Symbol.isConcatSpreadable`可改变默认行为。

| `Symbol.isConcatSpreadable` 的属性特性 |
| --- |
| 可写 | 否 |
| 可枚举 | 否 |
| 可配置 | 否 |

示例
--

### 数组

默认情况下，`Array.prototype.concat()` 展开其元素连接到结果中：

```js
var alpha = ["a", "b", "c"],
  numeric = [1, 2, 3];

var alphaNumeric = alpha.concat(numeric);

console.log(alphaNumeric); // 结果：['a', 'b', 'c', 1, 2, 3]
```

设置`Symbol.isConcatSpreadable`为`false`：

```js
var alpha = ["a", "b", "c"],
  numeric = [1, 2, 3];

numeric[Symbol.isConcatSpreadable] = false;
var alphaNumeric = alpha.concat(numeric);

console.log(alphaNumeric); // 结果：['a', 'b', 'c', [1, 2, 3] ]
```

### Array-like 对象

对于类数组 (array-like) 对象，默认不展开。期望展开其元素用于连接，需要设置 `Symbol.isConcatSpreadable` 为 true：

```js
var x = [1, 2, 3];

var fakeArray = {
  [Symbol.isConcatSpreadable]: true,
  length: 2,
  0: "hello",
  1: "world",
};

x.concat(fakeArray); // [1, 2, 3, "hello", "world"]
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-symbol.isconcatspreadable |

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

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

参考
--

* `Array.prototype.concat()`


Symbol.prototype.toString() - JavaScript

Symbol.prototype.toString()
===========================

概述
--

**`toString()`** 方法返回当前 symbol 对象的字符串表示。

语法
--
```

symbol.toString();
```

描述
--

`Symbol` 对象拥有自己的 `toString` 方法，因而遮蔽了原型链上的 `Object.prototype.toString()`。

### symbol 原始值不能转换为字符串

symbol 原始值不能转换为字符串，所以只能先转换成它的包装对象，再调用 `toString()` 方法：

```js
Symbol("foo") + "bar";
// TypeError: Can't convert symbol to string
Symbol("foo").toString() + "bar";
// "Symbol(foo)bar"，就相当于下面的：
Object(Symbol("foo")).toString() + "bar";
// "Symbol(foo)bar"
```

示例
--

```js
Symbol("desc").toString(); // "Symbol(desc)"

// 内置通用（well-known）symbol
Symbol.iterator.toString(); // "Symbol(Symbol.iterator)

// global symbols
Symbol.for("foo").toString(); // "Symbol(foo)"
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-symbol.prototype.tostring |

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

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

参见
--

* `Object.prototype.toString()`


Symbol.matchAll - JavaScript

Symbol.matchAll
===============

**`Symbol.matchAll`** 内置通用（well-known）符号指定方法返回一个迭代器，该迭代器根据字符串生成正则表达式的匹配项。此函数可以被 `String.prototype.matchAll()` 方法调用。

详细信息请参阅 `RegExp.prototype[@@matchAll]()` 和 `String.prototype.matchAll()`。

尝试一下
----

值
-

内置通用符号 `@@matchAll`。

| `Symbol.matchAll` 的属性特性 |
| --- |
| 可写 | 否 |
| 可枚举 | 否 |
| 可配置 | 否 |

示例
--

### 使用 Symbol.matchAll

```js
const str = "2016-01-02|2019-03-07";
const numbers = {
  \*[Symbol.matchAll](str) {
    for (const n of str.matchAll(/[0-9]+/g)) yield n[0];
  },
};
console.log(Array.from(str.matchAll(numbers)));
// ["2016", "01", "02", "2019", "03", "07"]
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-symbol.matchall |

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

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

参见
--

* `core-js` 中 `Symbol.matchAll` 的 polyfill
* `String.prototype.matchAll()`
* `RegExp.prototype[@@matchAll]()`


Symbol.iterator - JavaScript

Symbol.iterator
===============

**`Symbol.iterator`** 为每一个对象定义了默认的迭代器。该迭代器可以被 `for...of` 循环使用。

尝试一下
----

描述
--

当需要对一个对象进行迭代时（比如开始用于一个 `for..of` 循环中），它的 `@@iterator` 方法都会在不传参情况下被调用，返回的**迭代器**用于获取要迭代的值。

一些内置类型拥有默认的迭代器行为，其他类型（如 `Object`）则没有。拥有默认的 `@@iterator` 方法的内置类型是：

* `Array.prototype[@@iterator]()`
* `TypedArray.prototype[@@iterator]()`
* `String.prototype[@@iterator]()`
* `Map.prototype[@@iterator]()`
* `Set.prototype[@@iterator]()`

更多信息请参见迭代协议。

| `Symbol.iterator` 的属性特性 |
| --- |
| 可写 | 否 |
| 可枚举 | 否 |
| 可配置 | 否 |

示例
--

### 自定义迭代器

我们可以像下面这样创建自定义的迭代器：

```js
const myIterable = {};
myIterable[Symbol.iterator] = function\* () {
  yield 1;
  yield 2;
  yield 3;
};
[...myIterable]; // [1, 2, 3]
```

或者可以在类或对象中使用计算属性定义迭代：

```js
class Foo {
  \*[Symbol.iterator]() {
    yield 1;
    yield 2;
    yield 3;
  }
}

const someObj = {
  \*[Symbol.iterator]() {
    yield "a";
    yield "b";
  },
};

console.log(...new Foo()); // 1, 2, 3
console.log(...someObj); // 'a', 'b'
```

### 不符合标准的迭代器

如果一个迭代器 `@@iterator` 没有返回一个迭代器对象，那么它就是一个不符合标准的迭代器。这样的迭代器将会在运行期抛出异常，甚至出现非常诡异的 Bug：

```js
const nonWellFormedIterable = {};
nonWellFormedIterable[Symbol.iterator] = () => 1;
[...nonWellFormedIterable]; // TypeError: [Symbol.iterator]() returned a non-object value
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-symbol.iterator |

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

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

参见
--

* `core-js` 中 `Symbol.iterator` 的 Polyfill
* 迭代协议
* `Array.prototype[@@iterator]()`
* `TypedArray.prototype[@@iterator]()`
* `String.prototype[@@iterator]()`
* `Map.prototype[@@iterator]()`
* `Set.prototype[@@iterator]()`


Symbol.hasInstance - JavaScript

Symbol.hasInstance
==================

**`Symbol.hasInstance`** 用于判断某对象是否为某构造器的实例。因此你可以用它自定义 `instanceof` 操作符在某个类上的行为。

尝试一下
----

| `Symbol.hasInstance` 的属性特性 |
| --- |
| 可写 | 否 |
| 可枚举 | 否 |
| 可配置 | 否 |

示例
--

你可实现一个自定义的`instanceof` 行为，例如：

```js
class MyArray {
  static [Symbol.hasInstance](instance) {
    return Array.isArray(instance);
  }
}
console.log([] instanceof MyArray); // true
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-symbol.hasinstance |

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

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

参见
--

* `instanceof`


Symbol.keyFor() - JavaScript

Symbol.keyFor()
===============

概述
--

**`Symbol.keyFor(sym)`** 方法用来获取全局 symbol 注册表中与某个 symbol 关联的键。

语法
--
```

Symbol.keyFor(sym);
```

### 参数

sym

必选参数，需要查找键值的某个 Symbol。

### 返回值

如果全局注册表中查找到该 symbol，则返回该 symbol 的 key 值，返回值为字符串类型。否则返回 undefined

示例
--

```js
// 创建一个全局 Symbol
var globalSym = Symbol.for("foo");
Symbol.keyFor(globalSym); // "foo"

var localSym = Symbol();
Symbol.keyFor(localSym); // undefined，

// 以下 Symbol 不是保存在全局 Symbol 注册表中
Symbol.keyFor(Symbol.iterator); // undefined
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-symbol.keyfor |

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

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

参见
--

* `Symbol.prototype.for()`


Symbol.toStringTag - JavaScript

Symbol.toStringTag
==================

**`Symbol.toStringTag`** 内置通用（well-known）symbol 是一个字符串值属性，用于创建对象的默认字符串描述。它由 `Object.prototype.toString()` 方法内部访问。

尝试一下
----

| `Symbol.toStringTag` 的属性特性 |
| --- |
| 可写 | 否 |
| 可枚举 | 否 |
| 可配置 | 否 |

示例
--

### 默认标签

一些值没有 `Symbol.toStringTag`，但是有额外的 `toString()` 表示。有关完整列表，请参见 `Object.prototype.toString()`。

```js
Object.prototype.toString.call("foo"); // "[object String]"
Object.prototype.toString.call([1, 2]); // "[object Array]"
Object.prototype.toString.call(3); // "[object Number]"
Object.prototype.toString.call(true); // "[object Boolean]"
Object.prototype.toString.call(undefined); // "[object Undefined]"
Object.prototype.toString.call(null); // "[object Null]"
// ... and more
```

### 内置的 toStringTag symbol

大多数内置的对象提供了它们自己的 `@@toStringTag` 属性。所有内置对象的 `@@toStringTag` 属性都是不可写的（writable）、不可枚举的（enumerable）但是可配置的（configurable）。

```js
Object.prototype.toString.call(new Map()); // "[object Map]"
Object.prototype.toString.call(function\* () {}); // "[object GeneratorFunction]"
Object.prototype.toString.call(Promise.resolve()); // "[object Promise]"
// ... and more
```

### 使用 toStringTag 自定义标签

当创建你自己的类时，JavaScript 默认用“Object”标签：

```js
class ValidatorClass {}

Object.prototype.toString.call(new ValidatorClass()); // "[object Object]"
```

现在，在 `toStringTag` 的帮助下，你可以设置自己的自定义标签：

```js
class ValidatorClass {
  get [Symbol.toStringTag]() {
    return "Validator";
  }
}

Object.prototype.toString.call(new ValidatorClass()); // "[object Validator]"
```

### toStringTag 适用于所有 DOM 原型对象

由于 2020 年年中 WebIDL 规范的变化，浏览器正在向所有 DOM 原型对象添加 `Symbol.toStringTag` 属性。例如，要访问 `HTMLButtonElement` 上的 `Symbol.toStringTag` 属性：

```js
const test = document.createElement("button");
test.toString(); // Returns [object HTMLButtonElement]
test[Symbol.toStringTag]; // Returns HTMLButtonElement
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-symbol.tostringtag |

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

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

参见
--

* `core-js` 中 `Symbol.toStringTag` 的 polyfill
* `Object.prototype.toString()`


Generator.prototype.throw() - JavaScript

Generator.prototype.throw()
===========================

`Generator` 实例的 **`throw()`** 方法的作用就好像一个 `throw` 语句被插入到生成器主体的当前暂停位置，这会通知生成器错误的情况并允许其处理错误，或者执行清理和自行关闭。

语法
--

```js
generatorInstance.throw(exception)
```

### 参数

`exception`

要抛出的异常。使用 `Error` 实例对调试非常有帮助。

### 返回值

如果抛出的异常被 `try...catch` 捕获并且生成器恢复生成更多的值，它将返回一个 `Object`，带有两个属性：

`done`

一个布尔值：

* 如果该生成器函数的控制流程已经结束，则为 `true`。
* 如果该生成器函数还能产生更多的值，则为 `false`。

`value`

下一个 `yeild` 表达式生成的值。

### 异常

如果抛出的错误没有被 `try...catch` 捕获，则传给 `throw()` 的 `exception` 将从生成器函数中抛出。

描述
--

`throw()` 方法在被调用时，可以看作是在生成器主体当前暂停的位置插入了一个 `throw exception;` 语句，其中 `exception` 是传入给 `throw()` 方法的异常。因此，在典型的流程中，调用 `throw(exception)` 将会导致生成器抛出异常。然而，如果 `yield` 表达式被包含在 `try...finally` 块中，错误可能会被捕获，并且控制流可以在错误处理后恢复，或者正常退出。

示例
--

### 使用 throw()

以下示例展示了一个简单生成器和使用 `throw` 方法抛出的错误。像往常一样，错误可以通过 `try...catch` 块捕获。

```js
function\* gen() {
  while (true) {
    try {
      yield 42;
    } catch (e) {
      console.log("捕获到错误！");
    }
  }
}

const g = gen();
g.next();
// { value: 42, done: false }
g.throw(new Error("出现了些问题"));
// "捕获到错误！"
// { value: 42, done: false }
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-generator.prototype.throw |

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

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

参见
--

* `function*`


Generator.prototype.next() - JavaScript

Generator.prototype.next()
==========================

`Generator` 实例的 **`next()`** 方法返回一个包含属性 `done` 和 `value` 的对象。你也可以通过向 `next` 方法传入一个参数来向生成器传一个值。

语法
--

```js
next()
next(value)
```

### 参数

`value` 可选

要传给生成器的值。

该值将作为 `yield` 表达式的结果。例如，在 `variable = yield expression` 中，传入给 `.next()` 函数的值将被分配给 `variable`。

### 返回值

一个 `Object`，包含以下两个属性：

`done`

一个布尔值：

* 如果生成器的控制流程已经结束，则为 `true`。在这种情况下，`value` 指定生成器的*返回值*（可能是 undefined）。
* 如果生成器还是产生更多的值，则为 `false`。

`value`

生成器产生或返回的任何 JavaScript 值。

示例
--

### 使用 next()

下面的例子展示了一个简单的生成器，以及调用 `next` 方法后的返回值：

```js
function\* gen() {
  yield 1;
  yield 2;
  yield 3;
}

const g = gen(); // Generator { }
g.next(); // { value: 1, done: false }
g.next(); // { value: 2, done: false }
g.next(); // { value: 3, done: false }
g.next(); // { value: undefined, done: true }
```

### 将 next() 与列表一起使用

在这个示例中，`getPage` 获取一个列表并将其按 `pageSize` 大小“分页”成块。每次调用 `next` 都会产生一个这样的块。

```js
function\* getPage(list, pageSize = 1) {
  for (let index = 0; index < list.length; index += pageSize) {
    yield list.slice(index, index + pageSize);
  }
}

const list = [1, 2, 3, 4, 5, 6, 7, 8];
const page = getPage(list, 3); // Generator { }

page.next(); // { value: [1, 2, 3], done: false }
page.next(); // { value: [4, 5, 6], done: false }
page.next(); // { value: [7, 8], done: false }
page.next(); // { value: undefined, done: true }
```

### 向生成器传值

在此示例中，调用 `next` 时传入值。

**备注：** 第一次调用不会打印任何内容，因为生成器最初没有产生任何内容。

```js
function\* gen() {
  while (true) {
    const value = yield;
    console.log(value);
  }
}

const g = gen();
g.next(1); // 返回 { value: undefined, done: false }
// 这一步不会有输出：通过 `next` 发送的第一个值会被丢弃
g.next(2); // 返回 { value: undefined, done: false }
// 打印 2
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-generator.prototype.next |

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

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

参见
--

* `function*`
* 迭代器和生成器指南


Generator.prototype.return() - JavaScript

Generator.prototype.return()
============================

`Generator` 实例的 **`return()`** 方法的作用就好像一个 `return` 语句被插入到生成器主体的当前暂停位置，这会结束生成器并允许生成器与 `try...finally` 块结合使用时执行任何清理任务。

语法
--

```js
generatorInstance.return()
generatorInstance.return(value)
```

### 参数

`value` 可选

要返回的值。

### 返回值

一个 `Object`，有以下两个属性：

`done`

一个布尔值：

* 如果该生成器函数的控制流程已经结束，则为 `true`。
* 如果该生成器函数的控制流程还未结束并能产生更多的值，则为 `false`。只有在 `try...finally` 中捕获 `return` 并且 `finally` 块中还有更多的 `yield` 表达式时才会发生这种情况。

`value`

给定的作为参数的值，或者，如果 `yield` 表达式包含在 `try...finally`，从 `finally` 块产生/返回的值。

描述
--

`return()` 方法在被调用时，可以看作是在生成器主体当前暂停的位置插入了一个 `return value;` 语句，其中 `value` 是传入给 `return()` 方法的值。因此，在典型的流程中，调用 `return(value)` 将返回 `{done: true, value: value }`。然而，如果 `yield` 表达式被包含在 `try...finally` 块中，控制流不会退出函数体，而是进入 `finally` 块。在这种情况下，如果 `finally` 块中有更多 `yield` 表达式，返回的值可能会不同，`done` 甚至可能是 `false`。

示例
--

### 使用 return()

以下例子展示了一个简单的生成器和 `return` 方法的使用。

```js
function\* gen() {
  yield 1;
  yield 2;
  yield 3;
}

const g = gen();

g.next(); // { value: 1, done: false }
g.return("foo"); // { value: "foo", done: true }
g.next(); // { value: undefined, done: true }
```

如果对已经处于“完成”状态的生成器调用 `return(value)`，则生成器将保持在“完成”状态。

如果没有提供参数，则返回对象的 `value` 将为 `undefined`。如果提供了参数，则参数将被设置为返回对象的 `value` 属性的值，除非 `yield` 表达式被包装在 `try...finally` 中。

```js
function\* gen() {
  yield 1;
  yield 2;
  yield 3;
}

const g = gen();
g.next(); // { value: 1, done: false }
g.next(); // { value: 2, done: false }
g.next(); // { value: 3, done: false }
g.next(); // { value: undefined, done: true }
g.return(); // { value: undefined, done: true }
g.return(1); // { value: 1, done: true }
```

### 将 return() 与 try...finally 一起使用

如果 `yield` 表达式被包含在一个 `try...finally` 块中，只有生成器本身才能知道 `return` 方法已被调用。

当在 `try` 块中暂停的生成器上调用 `return` 方法时，生成器中的执行将继续到 `finally` 块——因为 `try...finally` 语句的 `finally` 块始终执行。

```js
function\* gen() {
  yield 1;
  try {
    yield 2;
    yield 3;
  } finally {
    yield "cleanup";
  }
}

const g1 = gen();
g1.next(); // { value: 1, done: false }

// 在 try...finally 前暂停执行。
g1.return("early return"); // { value: 'early return', done: true }

const g2 = gen();
g2.next(); // { value: 1, done: false }
g2.next(); // { value: 2, done: false }

// 在 try...finally 中暂停执行。
g2.return("early return"); // { value: 'cleanup', done: false }

// 完成值被保留
g2.next(); // { value: 'early return', done: true }

// 生成器处于完成状态
g2.return("not so early return"); // { value: 'not so early return', done: true }
```

finally 块的返回值也可以成为 `return` 调用返回结果的 `value`。

```js
function\* gen() {
  try {
    yield 1;
  } finally {
    return "cleanup";
  }
}

const g1 = gen();
g1.next(); // { value: 1, done: false }
g1.return("early return"); // { value: 'cleanup', done: true }
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-generator.prototype.return |

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

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

参见
--

* `function*`


GeneratorFunction() 构造函数 - JavaScript

GeneratorFunction() 构造函数
========================

**`GeneratorFunction()`** 构造函数创建 `GeneratorFunction` 对象。

请注意，`GeneratorFunction` *不是*全局对象。可以通过以下代码来获取它：

```js
const GeneratorFunction = function\* () {}.constructor;
```

`GeneratorFunction()` 构造函数不能直接使用，并且所有在 `Function()` 描述中提到的注意事项都适用于 `GeneratorFunction()`。

语法
--

```js
new GeneratorFunction(functionBody)
new GeneratorFunction(arg1, functionBody)
new GeneratorFunction(arg1, arg2, functionBody)
new GeneratorFunction(arg1, arg2, /\* …, \*/ argN, functionBody)

GeneratorFunction(functionBody)
GeneratorFunction(arg1, functionBody)
GeneratorFunction(arg1, arg2, functionBody)
GeneratorFunction(arg1, arg2, /\* …, \*/ argN, functionBody)
```

**备注：** 无论是否使用 `new` 调用 `GeneratorFunction()`，都可以创建一个新的 `GeneratorFunction` 实例。

### 参数

参见 `Function()`。

示例
--

### 创建和使用 GeneratorFunction() 构造函数

```js
const GeneratorFunction = function\* () {}.constructor;
const g = new GeneratorFunction("a", "yield a \* 2");
const iterator = g(10);
console.log(iterator.next().value); // 20
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-generatorfunction-constructor |

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

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

参见
--

* `function*`
* `function*` 表达式
* `Function()` 构造函数
* 迭代器和生成器指南
* Functions


Reflect.isExtensible() - JavaScript

Reflect.isExtensible()
======================

`静态方法 Reflect`**`.isExtensible()`** 判断一个对象是否可扩展（即是否能够添加新的属性）。与它 `Object.isExtensible()` 方法相似，但有一些不同，详情可见 与 Object.isExtensible() 的不同点。

语法
--
```

Reflect.isExtensible(target)
```

### 参数

`target`

检查是否可扩展的目标对象。

### 返回值

返回一个 `Boolean` 值表明该对象是否可扩展。

### 异常

抛出一个 `TypeError`，如果对象不是 `Object`。

描述
--

`Reflect.isExtensible 判断`一个对象是否可扩展（即是否能够添加新的属性）。它与 `Object.isExtensible()` 方法一样。

示例
--

### 使用 `Reflect.isExtensible()`

详情可见 `Object.isExtensible()`。

```js
// New objects are extensible.
var empty = {};
Reflect.isExtensible(empty); // === true

// ...but that can be changed.
Reflect.preventExtensions(empty);
Reflect.isExtensible(empty); // === false

// Sealed objects are by definition non-extensible.
var sealed = Object.seal({});
Reflect.isExtensible(sealed); // === false

// Frozen objects are also by definition non-extensible.
var frozen = Object.freeze({});
Reflect.isExtensible(frozen); // === false
```

### 与 `Object.isExtensible()` 的不同点

如果该方法的第一个参数不是一个对象（原始值），那么将造成一个 `TypeError` 异常。对于 `Object.isExtensible()`，非对象的第一个参数会被强制转换为一个对象。

```js
Reflect.isExtensible(1);
// TypeError: 1 is not an object

Object.isExtensible(1);
// false
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-reflect.isextensible |

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

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

参见
--

* `Reflect`
* `Object.isExtensible()`


Reflect.has() - JavaScript

Reflect.has()
=============

静态方法 **`Reflect.has()`** 作用与 `in` 操作符 相同。

句法
--
```

Reflect.has(target, propertyKey)
```

### 参数

`target`

目标对象。

`propertyKey`

属性名，需要检查目标对象是否存在此属性。

### 返回值

一个 `Boolean` 类型的对象指示是否存在此属性。

### 异常

如果目标对象并非`Object` 类型，抛出`TypeError`。

描述
--

`Reflect.has` 用于检查一个对象是否拥有某个属性，相当于`in` 操作符 。

示例
--

### 使用 `Reflect.has()`

```js
Reflect.has({ x: 0 }, "x"); // true
Reflect.has({ x: 0 }, "y"); // false

// 如果该属性存在于原型链中，返回 true
Reflect.has({ x: 0 }, "toString");

// Proxy 对象的 .has() 句柄方法
obj = new Proxy(
  {},
  {
    has(t, k) {
      return k.startsWith("door");
    },
  },
);
Reflect.has(obj, "doorbell"); // true
Reflect.has(obj, "dormitory"); // false
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-reflect.has |

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

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

相关连接
----

* `Reflect`
* `in` operator


Reflect.get() - JavaScript

Reflect.get()
=============

**`Reflect.get()`** 方法与从 对象 (`target[propertyKey]`) 中读取属性类似，但它是通过一个函数执行来操作的。

语法
--
```

Reflect.get(target, propertyKey[, receiver])
```

### 参数

`target`

需要取值的目标对象

`propertyKey`

需要获取的值的键值

receiver

如果`target`对象中指定了`getter`，`receiver`则为`getter`调用时的`this`值。

### 返回值

属性的值。

### 异常

如果目标值类型不是 `Object`，则抛出一个 `TypeError`。

描述
--

`Reflect.get`方法允许你从一个对象中取属性值。就如同属性访问器 语法，但却是通过函数调用来实现。

示例
--

### `使用 Reflect.get()`

```js
// Object
var obj = { x: 1, y: 2 };
Reflect.get(obj, "x"); // 1

// Array
Reflect.get(["zero", "one"], 1); // "one"

// Proxy with a get handler
var x = { p: 1 };
var obj = new Proxy(x, {
  get(t, k, r) {
    return k + "bar";
  },
});
Reflect.get(obj, "foo"); // "foobar"
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-reflect.get |

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

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

参见
--

* `Reflect`
* 属性访问器


Reflect.getOwnPropertyDescriptor() - JavaScript

Reflect.getOwnPropertyDescriptor()
==================================

静态方法 **`Reflect.getOwnPropertyDescriptor()`** 与 `Object.getOwnPropertyDescriptor()` 方法相似。如果在对象中存在，则返回给定的属性的属性描述符。否则返回 `undefined`。

语法
--
```

Reflect.getOwnPropertyDescriptor(target, propertyKey)
```

### 参数

`target`

需要寻找属性的目标对象。

`propertyKey`

获取自己的属性描述符的属性的名称。

### 返回值

如果属性存在于给定的目标对象中，则返回属性描述符；否则，返回 `undefined`。

### 异常

抛出一个 `TypeError`，如果目标不是 `Object`。

描述
--

`Reflect.getOwnPropertyDescriptor`方法返回一个属性描述符，如果给定的属性存在于对象中，否则返回 `undefined` 。与 `Object.getOwnPropertyDescriptor()` 的唯一不同在于如何处理非对象目标。

示例
--

### 使用 `Reflect.getOwnPropertyDescriptor()`

```js
Reflect.getOwnPropertyDescriptor({ x: "hello" }, "x");
// {value: "hello", writable: true, enumerable: true, configurable: true}

Reflect.getOwnPropertyDescriptor({ x: "hello" }, "y");
// undefined

Reflect.getOwnPropertyDescriptor([], "length");
// {value: 0, writable: true, enumerable: false, configurable: false}
```

### 与 `Object.getOwnPropertyDescriptor() 的不同点`

如果该方法的第一个参数不是一个对象（一个原始值），那么将造成 `TypeError` 错误。而对于 `Object.getOwnPropertyDescriptor`，非对象的第一个参数将被强制转换为一个对象处理。

```js
Reflect.getOwnPropertyDescriptor("foo", 0);
// TypeError: "foo" is not non-null object

Object.getOwnPropertyDescriptor("foo", 0);
// { value: "f", writable: false, enumerable: true, configurable: false }
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-reflect.getownpropertydescriptor |

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

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

参见
--

* `Reflect`
* `Object.getOwnPropertyDescriptor()`


Reflect.setPrototypeOf() - JavaScript

Reflect.setPrototypeOf()
========================

除了返回类型以外，静态方法 **`Reflect.setPrototypeOf()`** 与 `Object.setPrototypeOf()` 方法是一样的。它可设置对象的原型（即内部的 `[[Prototype]]` 属性）为另一个对象或 `null`，如果操作成功返回 `true`，否则返回 `false`。

尝试一下
----

语法
--
```

Reflect.setPrototypeOf(target, prototype)
```

### 参数

*`target`*

设置原型的目标对象。

*`prototype`*

对象的新原型（一个对象或 `null`）。

### 返回值

返回一个 `Boolean` 值表明是否原型已经成功设置。

### 异常

如果 *`target`* 不是 `Object` ，或 \*`prototype` \*既不是对象也不是 `null`，抛出一个 `TypeError` 异常。

描述
--

`Reflect.setPrototypeOf` 方法改变指定对象的原型（即，内部的 `[[Prototype]]` 属性值）。

示例
--

### 使用 `Reflect.setPrototypeOf()`

```js
Reflect.setPrototypeOf({}, Object.prototype); // true

// It can change an object's [[Prototype]] to null.
Reflect.setPrototypeOf({}, null); // true

// Returns false if target is not extensible.
Reflect.setPrototypeOf(Object.freeze({}), null); // false

// Returns false if it cause a prototype chain cycle.
var target = {};
var proto = Object.create(target);
Reflect.setPrototypeOf(target, proto); // false
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-reflect.setprototypeof |

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

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

参见
--

* `Reflect`
* `Object.setPrototypeOf()`


Reflect.apply() - JavaScript

Reflect.apply()
===============

静态方法 **`Reflect.apply()`** 通过指定的参数列表发起对目标 (target) 函数的调用。

尝试一下
----

语法
--
```

Reflect.apply(target, thisArgument, argumentsList)
```

### 参数

target

目标函数。

thisArgument

target 函数调用时绑定的 this 对象。

argumentsList

target 函数调用时传入的实参列表，该参数应该是一个类数组的对象。

### 返回值

返回值是调用完带着指定参数和 `this` 值的给定的函数后返回的结果。

### 异常

如果 `target` 对象不可调用，抛出 `TypeError`。

描述
--

该方法与 ES5 中`Function.prototype.apply()`方法类似：调用一个方法并且显式地指定 `this` 变量和参数列表 (arguments) ，参数列表可以是数组，或类似数组的对象。

```js
Function.prototype.apply.call(Math.floor, undefined, [1.75]);
```

使用 `Reflect.apply` 方法会使代码更加简洁易懂。

使用示例
----

### `Reflect.apply()`

```js
Reflect.apply(Math.floor, undefined, [1.75]);
// 1;

Reflect.apply(String.fromCharCode, undefined, [104, 101, 108, 108, 111]);
// "hello"

Reflect.apply(RegExp.prototype.exec, /ab/, ["confabulation"]).index;
// 4

Reflect.apply("".charAt, "ponies", [3]);
// "i"
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-reflect.apply |

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

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

相关连接
----

* `Reflect`
* `Function.prototype.apply()`


Reflect.preventExtensions() - JavaScript

Reflect.preventExtensions()
===========================

静态方法 **`Reflect.preventExtensions()`** 方法阻止新属性添加到对象 (例如：防止将来对对象的扩展被添加到对象中)。该方法与 `Object.preventExtensions()`相似，但有一些不同点。详情可见 differences。

尝试一下
----

语法
--
```

Reflect.preventExtensions(target)
```

### 参数

`target`

阻止扩展的目标对象。

### 返回值

返回一个 `Boolean` 值表明目标对象是否成功被设置为不可扩展。

### 异常

抛出一个 `TypeError` 错误，如果 `target` 不是 `Object`。

描述
--

`Reflect.preventExtensions` 方法阻止新属性添加到对象 (例如：防止将来对对象的扩展被添加到对象中)。该方法与 `Object.preventExtensions()` 方法相似。

示例
--

### 使用 `Reflect.preventExtensions()`

详情可见 `Object.preventExtensions()`.

```js
// Objects are extensible by default.
var empty = {};
Reflect.isExtensible(empty); // === true

// ...but that can be changed.
Reflect.preventExtensions(empty);
Reflect.isExtensible(empty); // === false
```

### 与 `Object.preventExtensions()` 的不同点

如果该方法的 *`target`* 参数不是一个对象（是原始值），那么将造成一个 `TypeError` 异常。对于`Object.preventExtensions()` 方法，非对象的 *`target`* 参数将被强制转换为对象。

```js
Reflect.preventExtensions(1);
// TypeError: 1 is not an object

Object.preventExtensions(1);
// 1
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-reflect.preventextensions |

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

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

参见
--

* `Reflect`
* `Object.isExtensible()`


Reflect.defineProperty() - JavaScript

Reflect.defineProperty()
========================

静态方法 **`Reflect.defineProperty()`** 基本等同于 `Object.defineProperty()` 方法，唯一不同是返回 `Boolean` 值。

语法
--
```

Reflect.defineProperty(target, propertyKey, attributes)
```

### 参数

`target`

目标对象。

`propertyKey`

要定义或修改的属性的名称。

`attributes`

要定义或修改的属性的描述。

### 返回值

`Boolean` 值指示了属性是否被成功定义。

### 异常

如果`target`不是 `Object`，抛出一个 `TypeError`。

描述
--

`Reflect.defineProperty` 方法允许精确添加或修改对象上的属性。更多的细节请参阅类似的`Object.defineProperty` 。

**备注：** `Object.defineProperty` 返回一个对象，或者如果属性没有被成功定义，抛出一个 `TypeError`。相比之下，`Reflect.defineProperty` 方法只返回一个 `Boolean`，来说明该属性是否被成功定义。

示例
--

### 使用 `Reflect.defineProperty()`
```

let obj = {}
Reflect.defineProperty(obj, 'x', {value: 7})  // true
obj.x                                         // 7
```

### 检查属性是否被成功定义

`Object.defineProperty` 方法，如果成功则返回一个对象，否则抛出一个 `TypeError` 。另外，当定义一个属性时，你也可以使用 `try...catch` 去捕获其中任何的错误。而因为 `Reflect.defineProperty` 返回 Boolean 值作为成功的标识，所以只能使用 `if...else` ：

```js
if (Reflect.defineProperty(target, property, attributes)) {
  // 成功
} else {
  // 失败
}
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-reflect.defineproperty |

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

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

参见
--

* `Reflect`
* `Object.defineProperty()`


Reflect.construct() - JavaScript

Reflect.construct()
===================

**`Reflect.construct()`** 方法的行为有点像 `new` 操作符 构造函数，相当于运行 `new target(...args)`.

语法
--
```

Reflect.construct(target, argumentsList[, newTarget])
```

### 参数

`target`

被运行的目标构造函数

`argumentsList`

类数组，目标构造函数调用时的参数。

`newTarget` 可选

作为新创建对象的原型对象的 `constructor` 属性，参考 `new.target` 操作符，默认值为 `target`。

### `返回值`

以`target`（如果`newTarget`存在，则为`newTarget`）函数为构造函数，`argumentList`为其初始化参数的对象实例。

### 异常

如果 target 或者 newTarget 不是构造函数，抛出`TypeError`,异常。

描述
--

`Reflect.construct`允许你使用可变的参数来调用构造函数，这和使用new 操作符搭配对象展开符调用一样。

```js
var obj = new Foo(...args);
var obj = Reflect.construct(Foo, args);
```

### `Reflect.construct()` vs `Object.create()`

`在新语法 Reflect`出现之前，是通过明确指定构造函数和原型对象（使用`Object.create()`）来创建一个对象的。

```js
function OneClass() {
  this.name = "one";
}

function OtherClass() {
  this.name = "other";
}

// 创建一个对象：
var obj1 = Reflect.construct(OneClass, args, OtherClass);

// 与上述方法等效：
var obj2 = Object.create(OtherClass.prototype);
OneClass.apply(obj2, args);

console.log(obj1.name); // 'one'
console.log(obj2.name); // 'one'

console.log(obj1 instanceof OneClass); // false
console.log(obj2 instanceof OneClass); // false

console.log(obj1 instanceof OtherClass); // true
console.log(obj2 instanceof OtherClass); // true
```

虽然两种方式结果相同，但在创建对象过程中仍一点不同。

当使用`Object.create()`和`Function.prototype.apply()`时，如果不使用`new`操作符调用构造函数，构造函数内部的`new.target`值会指向`undefined`。

当调用`Reflect.construct()`来创建对象，`new.target`值会自动指定到`target`（或者 newTarget，前提是 newTarget 指定了）。

```js
function OneClass() {
  console.log("OneClass");
  console.log(new.target);
}
function OtherClass() {
  console.log("OtherClass");
  console.log(new.target);
}

var obj1 = Reflect.construct(OneClass, args);
// 输出：
// OneClass
// function OneClass { ... }

var obj2 = Reflect.construct(OneClass, args, OtherClass);
// 输出：
// OneClass
// function OtherClass { ... }

var obj3 = Object.create(OtherClass.prototype);
OneClass.apply(obj3, args);
// 输出：
// OneClass
// undefined
```

示例
--

### 使用 `Reflect.construct()`

```js
var d = Reflect.construct(Date, [1776, 6, 4]);
d instanceof Date; // true
d.getFullYear(); // 1776
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-reflect.construct |

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

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

参见
--

* `Reflect`
* `new`
* `new.target`


Reflect.set() - JavaScript

Reflect.set()
=============

静态方法 **`Reflect.set()`** 工作方式就像在一个对象上设置一个属性。

语法
--
```

Reflect.set(target, propertyKey, value[, receiver])
```

### 参数

`target`

设置属性的目标对象。

`propertyKey`

设置的属性的名称。

`value`

设置的值。

`receiver`

如果遇到 `setter`，`receiver`则为`setter`调用时的`this`值。

### 返回值

返回一个 `Boolean` 值表明是否成功设置属性。

### 异常

抛出一个 `TypeError`，如果目标不是 `Object`。

描述
--

`Reflect.set` 方法允许你在对象上设置属性。它的作用是给属性赋值并且就像 property accessor 语法一样，但是它是以函数的方式。

示例
--

### 使用 `Reflect.set()`

```js
// Object
var obj = {};
Reflect.set(obj, "prop", "value"); // true
obj.prop; // "value"

// Array
var arr = ["duck", "duck", "duck"];
Reflect.set(arr, 2, "goose"); // true
arr[2]; // "goose"

// It can truncate an array.
Reflect.set(arr, "length", 1); // true
arr; // ["duck"];

// With just one argument, propertyKey and value are "undefined".
var obj = {};
Reflect.set(obj); // true
Reflect.getOwnPropertyDescriptor(obj, "undefined");
// { value: undefined, writable: true, enumerable: true, configurable: true }
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-reflect.set |

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

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

参见
--

* `Reflect`
* 属性访问器


Reflect.deleteProperty() - JavaScript

Reflect.deleteProperty()
========================

静态方法 **`Reflect.deleteProperty()`** 允许用于删除属性。它很像 `delete` operator ，但它是一个函数。

语法
--
```

Reflect.deleteProperty(target, propertyKey)
```

### 参数

`target`

删除属性的目标对象。

`propertyKey`

需要删除的属性的名称。

### 返回值

`Boolean` 值表明该属性是否被成功删除。

### 异常

抛出一个 `TypeError`，如果`target`不是 `Object`。

描述
--

`Reflect.deleteProperty` 允许你删除一个对象上的属性。返回一个 `Boolean` 值表示该属性是否被成功删除。它几乎与非严格的 `delete` operator 相同。

示例
--

### 使用 `Reflect.deleteProperty()`

```js
var obj = { x: 1, y: 2 };
Reflect.deleteProperty(obj, "x"); // true
obj; // { y: 2 }

var arr = [1, 2, 3, 4, 5];
Reflect.deleteProperty(arr, "3"); // true
arr; // [1, 2, 3, , 5]

// 如果属性不存在，返回 true
Reflect.deleteProperty({}, "foo"); // true

// 如果属性不可配置，返回 false
Reflect.deleteProperty(Object.freeze({ foo: 1 }), "foo"); // false
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-reflect.deleteproperty |

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

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

参见
--

* `core-js` 中 `Reflect.deleteProperty` 的 polyfill
* `Reflect`
* `delete`
* `handler.deleteProperty()`


Reflect.getPrototypeOf() - JavaScript

Reflect.getPrototypeOf()
========================

静态方法 **`Reflect.getPrototypeOf()`** 与 `Object.getPrototypeOf()` 方法几乎是一样的。都是返回指定对象的原型（即内部的 `[[Prototype]]` 属性的值）。

尝试一下
----

语法
--
```

Reflect.getPrototypeOf(target)
```

### 参数

`target`

获取原型的目标对象。

### 返回值

给定对象的原型。如果给定对象没有继承的属性，则返回 `null`。

### 异常

如果 `target` 不是 `Object`，抛出一个 `TypeError` 异常。

描述
--

`Reflect.getPrototypeOf` 返回指定对象的原型 (即内部的 `[[Prototype]]` 属性的值) 。

示例
--

### 使用 `Reflect.getPrototypeOf()`

```js
Reflect.getPrototypeOf({}); // Object.prototype
Reflect.getPrototypeOf(Object.prototype); // null
Reflect.getPrototypeOf(Object.create(null)); // null
```

### 与 `Object.getPrototypeOf()` 比较
```

// 如果参数为 Object，返回结果相同
Object.getPrototypeOf({})   // Object.prototype
Reflect.getPrototypeOf({})  // Object.prototype

// 在 ES5 规范下，对于非 Object，抛异常
Object.getPrototypeOf('foo')   // Throws TypeError
Reflect.getPrototypeOf('foo')  // Throws TypeError

// 在 ES2015 规范下，Reflect 抛异常，Object 强制转换非 Object
Object.getPrototypeOf('foo')   // String.prototype
Reflect.getPrototypeOf('foo')  // Throws TypeError

// 如果想要模拟 Object 在 ES2015 规范下的表现，需要强制类型转换
Reflect.getPrototypeOf(Object('foo'))  // String.prototype
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-reflect.getprototypeof |

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

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

参见
--

* `Reflect`
* `Object.getPrototypeOf()`


Reflect.ownKeys() - JavaScript

Reflect.ownKeys()
=================

静态方法 **`Reflect.ownKeys()`** 返回一个由目标对象自身的属性键组成的数组。

尝试一下
----

语法
--
```

Reflect.ownKeys(target)
```

### 参数

`target`

获取自身属性键的目标对象。

### 返回值

由目标对象的自身属性键组成的 `Array`。

### 异常

如果目标不是 `Object`，抛出一个 `TypeError`。

描述
--

`Reflect.ownKeys` 方法返回一个由目标对象自身的属性键组成的数组。它的返回值等同于 ``Object.getOwnPropertyNames`(target).concat(`Object.getOwnPropertySymbols`(target))`。

示例
--

### 使用 `Reflect.ownKeys()`

```js
Reflect.ownKeys({ z: 3, y: 2, x: 1 }); // [ "z", "y", "x" ]
Reflect.ownKeys([]); // ["length"]

var sym = Symbol.for("comet");
var sym2 = Symbol.for("meteor");
var obj = {
  [sym]: 0,
  str: 0,
  773: 0,
  0: 0,
  [sym2]: 0,
  "-1": 0,
  8: 0,
  "second str": 0,
};
Reflect.ownKeys(obj);
// [ "0", "8", "773", "str", "-1", "second str", Symbol(comet), Symbol(meteor) ]
// Indexes in numeric order,
// strings in insertion order,
// symbols in insertion order
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-reflect.ownkeys |

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

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

参见
--

* `Reflect`
* `Object.getOwnPropertyNames()`


getter - JavaScript

getter
======

**`get`** 语法将对象属性绑定到查询该属性时将被调用的函数。

尝试一下
----

语法
--
```

{get prop() { ... } }

{get [expression]() { ... } }
```

### 参数

`prop`

要绑定到给定函数的属性名。

expression

从 ECMAScript 2015 开始，还可以使用一个计算属性名的表达式绑定到给定的函数。

描述
--

有时需要允许访问返回动态计算值的属性，或者你可能需要反映内部变量的状态，而不需要使用显式方法调用。在 JavaScript 中，可以使用 *getter* 来实现。

尽管可以结合使用 getter 和 setter 来创建一个伪属性，但是不可能同时将一个 getter 绑定到一个属性并且该属性实际上具有一个值。

使用`get`语法时应注意以下问题：

* 可以使用数值或字符串作为标识；
* 必须不带参数（请参考Incompatible ES5 change: literal getter and setter functions must now have exactly zero or one arguments）；
* 它不能与另一个 `get` 或具有相同属性的数据条目同时出现在一个对象字面量中（不允许使用 `{ get x() { }, get x() { } }` 和 `{ x: ..., get x() { } }`）。
示例
--

### 在新对象初始化时定义一个 getter

这会为`obj`创建一个伪属性`latest`，它会返回`log`数组的最后一个元素。

```js
const obj = {
  log: ["example", "test"],
  get latest() {
    if (this.log.length == 0) return undefined;
    return this.log[this.log.length - 1];
  },
};
console.log(obj.latest); // "test".
```

注意，尝试为`latest`分配一个值不会改变它。

### 使用`delete`操作符删除 getter

只需使用 `delete`，就可删除 getter：

```js
delete obj.latest;
```

### 使用`defineProperty`在现有对象上定义 getter

要随时将 getter 添加到现有对象，使用 `Object.defineProperty()`.

```js
var o = { a: 0 };

Object.defineProperty(o, "b", {
  get: function () {
    return this.a + 1;
  },
});

console.log(o.b); // Runs the getter, which yields a + 1 (which is 1)
```

### 使用计算出的属性名

```js
var expr = "foo";

var obj = {
  get [expr]() {
    return "bar";
  },
};

console.log(obj.foo); // "bar"
```

### 智能 / 自我复写/ 懒加载 getters

Getters 给你一种方法来定义一个对象的属性，但是在访问它们之前不会计算属性的值。getter 延迟计算值的成本，直到需要此值，如果不需要，你就不用支付成本。

一种额外的优化技术是用**智能 (或称记忆化)getters** 延迟属性值的计算并将其缓存以备以后访问。该值是在第一次调用 getter 时计算的，然后被缓存，因此后续访问返回缓存值而不重新计算它。这在以下情况下很有用：

* 如果属性值的计算是昂贵的（占用大量 RAM 或 CPU 时间，产生工作线程，检索远程文件等）。
* 如果现在不需要该值。它将在稍后使用，或在某些情况下它根本不使用。
* 如果被使用，它将被访问几次，并且不需要重新计算，该值将永远不会被改变，或者不应该被重新计算。

**备注：** 这意味着你不应该为你希望更改其值的属性使用懒 getter，因为 getter 不会重新计算该值。

在以下示例中，对象具有一个 getter 属性。在获取属性时，该属性将从对象中删除并重新添加，但此时将隐式显示为数据属性。最后返回得到值。

```js
get notifier() {
  delete this.notifier;
  return this.notifier = document.getElementById('bookmarked-notification-anchor');
},
```

对于 Firefox 代码，另请参阅定义`defineLazyGetter()` 函数的`XPCOMUtils.jsm`代码模块。

### `get` vs. `defineProperty`

当使用 `get` 关键字时，它和`Object.defineProperty()` 有类似的效果，在`classes`中使用时，二者有细微的差别。

当使用 `get` 关键字时，属性将被定义在实例的原型上，当使用`Object.defineProperty()`时，属性将被定义在实例自身上。

```js
class Example {
  get hello() {
    return "world";
  }
}

const obj = new Example();
console.log(obj.hello);
// "world"

console.log(Object.getOwnPropertyDescriptor(obj, "hello"));
// undefined

console.log(
  Object.getOwnPropertyDescriptor(Object.getPrototypeOf(obj), "hello"),
);
// { configurable: true, enumerable: false, get: function get hello() { return 'world'; }, set: undefined }
```

规范
--

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

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

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

参见
--

* setter
* `delete`
* `Object.defineProperty()`
* `Object.prototype.__defineGetter__()`
* `Object.prototype.__defineSetter__()`
* 在 Javascript 指南中 定义 Getter 和 Setter


方法的定义 - JavaScript

方法的定义
=====

从 ECMAScript 2015 开始，在对象初始器中引入了一种更简短定义方法的语法，这是一种把方法名直接赋给函数的简写方式。

尝试一下
----

语法
--
```

var obj = {
  property( parameters… ) {},
  *generator( parameters… ) {},
  async property( parameters… ) {},
  async* generator( parameters… ) {},

  // with computed keys:
  [property]( parameters… ) {},
  *[generator]( parameters… ) {},
  async [property]( parameters… ) {},

  // compare getter/setter syntax:
  get property() {},
  set property(value) {}
};
```

描述
--

该简写语法与 ECMAScript 2015 的getter和setter语法类似。

如下代码：

```js
var obj = {
  foo: function () {
    /\* code \*/
  },
  bar: function () {
    /\* code \*/
  },
};
```

现可被简写为：

```js
var obj = {
  foo() {
    /\* code \*/
  },
  bar() {
    /\* code \*/
  },
};
```

**备注：** 简写语法使用具名函数而不是匿名函数（如…`foo: function() {}`…）。具名函数可以从函数体调用（这对匿名函数是不可能的，因为没有标识符可以引用）。详细信息，请参阅`function`。

### 生成器方法

生成器方法也可以用这种简写语法定义。使用它们时，

* 简写语法中的星号（\*）必须出现在生成器名前，也就是说`* g(){}`可以正常工作，而`g *(){}`不行。
* 非生成器方法定义可能不包含`yield`关键字。这意味着遗留的生成器函数 (en-US)也不会工作，并且将抛出 `SyntaxError`。始终使用`yield`与星号（\*）结合使用。

```js
// 用有属性名的语法定义方法（ES6 之前）：
var obj2 = {
  g: function\* () {
    var index = 0;
    while (true) yield index++;
  },
};

// 同一个方法，简写语法：
var obj2 = {
  \*g() {
    var index = 0;
    while (true) yield index++;
  },
};

var it = obj2.g();
console.log(it.next().value); // 0
console.log(it.next().value); // 1
```

### Async 方法

Async 方法也可以使用简写语法来定义。

```js
// 用有属性名的语法定义方法（ES6 之前）：
var obj3 = {
  f: async function () {
    await some_promise;
  },
};

// 同一个方法，简写语法：
var obj3 = {
  async f() {
    await some_promise;
  },
};
```

### Async 生成器方法

生成器方法也能成为 async.

```js
var obj4 = {
  f: async function\* () {
    yield 1;
    yield 2;
    yield 3;
  },
};

// The same object using shorthand syntax
var obj4 = {
  async \*f() {
    yield 1;
    yield 2;
    yield 3;
  },
};
```

### 方法定义不是构造函数

所有方法定义不是构造函数，如果你尝试实例化它们，将抛出`TypeError`。

```js
var obj = {
  method() {},
};
new obj.method(); // TypeError: obj.method is not a constructor

var obj = {
  \*g() {},
};
new obj.g(); // TypeError: obj.g is not a constructor (changed in ES2016)
```

示例
--

### 简单示例

```js
var obj = {
  a: "foo",
  b() {
    return this.a;
  },
};
console.log(obj.b()); // "foo"
```

### 计算的属性名

该简写语法还支持计算的属性名称作为方法名。

```js
var bar = {
  foo0: function () {
    return 0;
  },
  foo1() {
    return 1;
  },
  ["foo" + 2]() {
    return 2;
  },
};

console.log(bar.foo0()); // 0
console.log(bar.foo1()); // 1
console.log(bar.foo2()); // 2
```

规范
--

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

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

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

参见
--

* `get`
* `set`
* Lexical grammar


Arguments 对象 - JavaScript

Arguments 对象
============

**`arguments`** 是一个对应于传递给函数的参数的类数组对象。

尝试一下
----

描述
--

**备注：** 如果你编写兼容 ES6 的代码，那么优先推荐使用 剩余参数

**备注：** “类数组”意味着 `arguments` 有 `长度` 属性 并且属性的索引是从零开始的，但是它没有 `Array`的 内置方法，例如 `forEach()` 和 `map()`都是没有的。详情可以看 §Description.

`arguments`对象是所有（非箭头）函数中都可用的**局部变量**。你可以使用`arguments`对象在函数中引用函数的参数。此对象包含传递给函数的每个参数，第一个参数在索引 0 处。例如，如果一个函数传递了三个参数，你可以以如下方式引用他们：

```js
arguments[0];
arguments[1];
arguments[2];
```

### 通过索引赋值

参数也可以被设置：

```js
arguments[1] = "new value";
```

`arguments`对象不是一个 `Array` 。它类似于`Array`，但除了 length 属性和索引元素之外没有任何`Array`属性。例如，它没有 pop 方法。但是它可以被转换为一个真正的`Array`：

```js
var args = Array.prototype.slice.call(arguments);
var args = [].slice.call(arguments);

// ES2015
const args = Array.from(arguments);
const args = [...arguments];
```

**警告：** 对参数使用 slice 会阻止某些 JavaScript 引擎中的优化 (比如 V8 - 更多信息)。如果你关心性能，尝试通过遍历 arguments 对象来构造一个新的数组。另一种方法是使用被忽视的`Array`构造函数作为一个函数：

```js
var args =
  arguments.length === 1 ? [arguments[0]] : Array.apply(null, arguments);
```

如果调用的参数多于正式声明接受的参数，则可以使用`arguments`对象。这种技术对于可以传递可变数量的参数的函数很有用。使用 `arguments.length`来确定传递给函数参数的个数，然后使用`arguments`对象来处理每个参数。要确定函数签名中（输入）参数的数量，请使用`Function.length`属性。

### 对参数使用 `typeof`

typeof 参数返回 'object'。

```js
console.log(typeof arguments); // 'object'
// arguments 对象只能在函数内使用
function test(a) {
  console.log(a, Object.prototype.toString.call(arguments));
  console.log(arguments[0], arguments[1]);
  console.log(typeof arguments[0]);
}
test(1);
/\*
1 "[object Arguments]"
1 undefined
number
\*/
```

可以使用索引确定单个参数的类型。

```js
console.log(typeof arguments[0]); //this will return the typeof individual arguments.
```

### 对参数使用扩展语法

你还可以使用`Array.from()`方法或扩展运算符将参数转换为真实数组：

```js
var args = Array.from(arguments);
var args = [...arguments];
```

属性
--

`arguments.callee`

指向参数所属的当前执行的函数。

`arguments.length`

传递给函数的参数数量。

`arguments[@@iterator]`

返回一个新的 Array 迭代器对象，该对象包含参数中每个索引的值。

示例
--

### 遍历参数求和

```js
function add() {
  var sum = 0,
    len = arguments.length;
  for (var i = 0; i < len; i++) {
    sum += arguments[i];
  }
  return sum;
}
add(); // 0
add(1); // 1
add(1, 2, 3, 4); // 10
```

### 定义连接字符串的函数

这个例子定义了一个函数来连接字符串。这个函数唯一正式声明了的参数是一个字符串，该参数指定一个字符作为衔接点来连接字符串。该函数定义如下：

```js
function myConcat(separator) {
  var args = Array.prototype.slice.call(arguments, 1);
  return args.join(separator);
}
```

你可以传递任意数量的参数到该函数，并使用每个参数作为列表中的项创建列表。

```js
// returns "red, orange, blue"
myConcat(", ", "red", "orange", "blue");

// returns "elephant; giraffe; lion; cheetah"
myConcat("; ", "elephant", "giraffe", "lion", "cheetah");

// returns "sage. basil. oregano. pepper. parsley"
myConcat(". ", "sage", "basil", "oregano", "pepper", "parsley");
```

### 定义创建 HTML 列表的方法

这个例子定义了一个函数通过一个字符串来创建 HTML 列表。这个函数唯一正式声明了的参数是一个字符。当该参数为 "`u`" 时，创建一个无序列表 (项目列表)；当该参数为 "`o`" 时，则创建一个有序列表 (编号列表)。该函数定义如下：

```js
function list(type) {
  var result = "<" + type + "l><li>";
  var args = Array.prototype.slice.call(arguments, 1);
  result += args.join("</li><li>");
  result += "</li></" + type + "l>"; // end list

  return result;
}
```

你可以传递任意数量的参数到该函数，并将每个参数作为一个项添加到指定类型的列表中。例如：

```js
var listHTML = list("u", "One", "Two", "Three");

/\* listHTML is:

"<ul><li>One</li><li>Two</li><li>Three</li></ul>"

\*/
```

### 剩余参数、默认参数和解构赋值参数

`arguments`对象可以与剩余参数、默认参数和解构赋值参数结合使用。

```js
function foo(...args) {
  return args;
}
foo(1, 2, 3); // [1,2,3]
```

在严格模式下，剩余参数、默认参数和解构赋值参数的存在不会改变 `arguments`对象的行为，但是在非严格模式下就有所不同了。

当非严格模式中的函数**没有**包含剩余参数、默认参数和解构赋值，那么`arguments`对象中的值**会**跟踪参数的值（反之亦然）。看下面的代码：

```js
function func(a) {
  arguments[0] = 99; // 更新了 arguments[0] 同样更新了 a
  console.log(a);
}
func(10); // 99
```

并且

```js
function func(a) {
  a = 99; // 更新了 a 同样更新了 arguments[0]
  console.log(arguments[0]);
}
func(10); // 99
```

当非严格模式中的函数**有**包含剩余参数、默认参数和解构赋值，那么`arguments`对象中的值**不会**跟踪参数的值（反之亦然）。相反，`arguments`反映了调用时提供的参数：

```js
function func(a = 55) {
  arguments[0] = 99; // updating arguments[0] does not also update a
  console.log(a);
}
func(10); // 10
```

并且

```js
function func(a = 55) {
  a = 99; // updating a does not also update arguments[0]
  console.log(arguments[0]);
}
func(10); // 10
```

并且

```js
function func(a = 55) {
  console.log(arguments[0]);
}
func(); // undefined
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-arguments-exotic-objects |

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

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

参见
--

* `Function`


箭头函数表达式 - JavaScript

箭头函数表达式
=======

**箭头函数表达式**的语法比传统的函数表达式更简洁，但在语义上有一些差异，在用法上也有一些限制：

* 箭头函数没有独立的 `this`、`arguments` 和 `super` 绑定 (en-US)，并且不可被用作方法。
* 箭头函数不能用作构造函数。使用 `new` 调用它们会引发 `TypeError`。它们也无法访问 `new.target` 关键字。
* 箭头函数不能在其主体中使用 `yield`，也不能作为生成器函数创建。
尝试一下
----

语法
--

```js
() => expression

param => expression

(param) => expression

(param1, paramN) => expression

() => {
  statements
}

param => {
  statements
}

(param1, paramN) => {
  statements
}
```

参数部分支持剩余参数、默认参数和解构赋值，并且始终需要使用括号：

```js
(a, b, ...r) => expression
(a = 400, b = 20, c) => expression
([a, b] = [10, 20]) => expression
({ a, b } = { a: 10, b: 20 }) => expression
```

箭头函数可以是 `async` 的，方法是在表达式前加上 `async` 关键字。

```js
async param => expression
async (param1, param2, ...paramN) => {
  statements
}
```

描述
--

让我们逐步将传统的匿名函数分解为最简单的箭头函数。每一步都是一个有效的箭头函数。

**备注：** 传统函数表达式和箭头函数除了语法上的区别外，还有更多的不同。我们将在接下来的几个小节中详细介绍它们的行为差异。

```js
// 传统匿名函数
(function (a) {
  return a + 100;
});

// 1. 移除“function”，并将箭头放置于参数和函数体起始大括号之间
(a) => {
  return a + 100;
};

// 2. 移除代表函数体的大括号和“return”——返回值是隐含的
(a) => a + 100;

// 3. 移除参数周围的括号
a => a + 100;
```

在上面的示例中，参数周围的括号和函数体周围的大括号都可以省略。但是，只有在某些情况下才能省略。

只有当函数只有一个简单参数时，才能省略括号。如果函数有多个参数、无参数、默认参数、重组参数或其余参数，则需要在参数列表周围加上括号。

```js
// 传统匿名函数
(function (a, b) {
  return a + b + 100;
});

// 箭头函数
(a, b) => a + b + 100;

const a = 4;
const b = 2;

// 传统无参匿名函数
(function () {
  return a + b + 100;
});

// 无参箭头函数
() => a + b + 100;
```

只有当函数直接返回表达式时，才可以省略大括号。如果函数体有额外的处理，则大括号是必需的，`return` 关键字也是必需的。箭头函数无法猜测函数体返回什么或何时返回。

```js
// 传统匿名函数
(function (a, b) {
  const chuck = 42;
  return a + b + chuck;
});

// 箭头函数
(a, b) => {
  const chuck = 42;
  return a + b + chuck;
};
```

箭头函数总是未命名的。如果箭头函数需要调用自身，请使用具名函数表达式。也可以将箭头函数赋值给一个变量，这样它就有了名字。

```js
// 传统函数
function bob(a) {
  return a + 100;
}

// 箭头函数
const bob2 = (a) => a + 100;
```

### 函数体

箭头函数既可以使用*表达式体*（expression body），也可以使用通常的*块体*（block body）。

在表达式体中，只需指定一个表达式，它将成为隐式返回值。在块体中，必须使用显式的 `return` 语句。

```js
const func = (x) => x \* x;
// 表达式体语法，隐含返回值

const func2 = (x, y) => {
  return x + y;
};
// 块体语法，需要明确返回值
```

使用表达式体语法 `(params) => { object: literal }` 返回对象字面量时，不能按预期工作。

```js
const func = () => { foo: 1 };
// 调用 func() 会返回 undefined！

const func2 = () => { foo: function () {} };
// SyntaxError: function statement requires a name

const func3 = () => { foo() {} };
// SyntaxError: Unexpected token '{'
```

这是因为只有当箭头后面的标记不是左括号时，JavaScript 才会将箭头函数视为表达式体，因此括号（{}）内的代码会被解析为一系列语句，其中 `foo` 是标签，而不是对象文字中的键。

要解决这个问题，可以用括号将对象字面量包装起来：

```js
const func = () => ({ foo: 1 });
```

### 不能用作方法

箭头函数表达式只能用于非方法函数，因为它们没有自己的 `this`。让我们看看将它们用作方法时会发生什么：

```js
"use strict";

const obj = {
  i: 10,
  b: () => console.log(this.i, this),
  c() {
    console.log(this.i, this);
  },
};

obj.b(); // 输出 undefined, Window { /\* … \*/ }（或全局对象）
obj.c(); // 输出 10, Object { /\* … \*/ }
```

另外一个示例涉及到 `Object.defineProperty()`：

```js
"use strict";

const obj = {
  a: 10,
};

Object.defineProperty(obj, "b", {
  get: () => {
    console.log(this.a, typeof this.a, this); // undefined 'undefined' Window { /\* … \*/ }（或全局对象）
    return this.a + 10; // 代表全局对象 'Window'，故 `this.a' 返回 'undefined'
  },
});
```

由于类体具有 `this` 上下文，因此作为类字段的箭头函数会关闭类的 `this` 上下文，箭头函数体中的 `this` 将正确指向实例（对于静态字段来说是类本身）。但是，由于它是一个闭包，而不是函数本身的绑定，因此 `this` 的值不会根据执行上下文而改变。

```js
class C {
  a = 1;
  autoBoundMethod = () => {
    console.log(this.a);
  };
}

const c = new C();
c.autoBoundMethod(); // 1
const { autoBoundMethod } = c;
autoBoundMethod(); // 1
// 如果这是普通方法，此时应该是 undefined
```

箭头函数属性通常被称作“自动绑定方法”，因为它与普通方法的等价性相同：

```js
class C {
  a = 1;
  constructor() {
    this.method = this.method.bind(this);
  }
  method() {
    console.log(this.a);
  }
}
```

**备注：** 类字段是在*实例*（instance）上定义的，而不是在*原型*（prototype）上定义的，因此每次创建实例都会创建一个新的函数引用并分配一个新的闭包，这可能会导致比普通非绑定方法更多的内存使用。

出于类似原因，`call()`、`apply()` 和 `bind()` 方法在箭头函数上调用时不起作用，因为箭头函数是根据箭头函数定义的作用域来建立 `this` 的，而 `this` 值不会根据函数的调用方式而改变。

### 没有参数绑定

箭头函数没有自己的 `arguments` 对象。因此，在本例中，`arguments` 是对外层作用域参数的引用：

```js
function foo(n) {
  const f = () => arguments[0] + n; // foo 的隐式参数绑定。arguments[0] 为 n
  return f();
}

foo(3); // 3 + 3 = 6
```

**备注：** 在严格模式下不能声明名为 `arguments` 的变量，因此上面的代码会出现语法错误。这使得 `arguments` 的范围效应更容易理解。

在大多数情况下，使用剩余参数是比使用 `arguments` 对象更好的选择。

```js
function foo(n) {
  const f = (...args) => args[0] + n;
  return f(10);
}

foo(1); // 11
```

### 不能用作构造函数

箭头函数不能用作构造函数，当使用 `new` 调用时会出错。它们也没有 `prototype` 属性。

```js
const Foo = () => {};
const foo = new Foo(); // TypeError: Foo is not a constructor
console.log("prototype" in Foo); // false
```

### 不能用作生成器

箭头函数的主体中不能使用 `yield` 关键字（除非在箭头函数进一步嵌套的生成器函数中使用）。因此，箭头函数不能用作生成器。

### 箭头前换行

箭头函数的参数和箭头之间不能换行。

```js
const func = (a, b, c)
  => 1;
// SyntaxError: Unexpected token '=>'
```

为便于格式化，可在箭头后换行，或在函数体周围使用括号/花括号，如下图所示。也可以在参数之间换行。

```js
const func = (a, b, c) =>
  1;

const func2 = (a, b, c) => (
  1
);

const func3 = (a, b, c) => {
  return 1;
};

const func4 = (
  a,
 b,
 c,
) => 1;
```

### 箭头的优先级

虽然箭头函数中的箭头不是运算符，但与普通函数相比，箭头函数具有特殊的解析规则，与运算符优先级的交互方式不同。

```js
let callback;

callback = callback || () => {};
// SyntaxError: invalid arrow-function arguments
```

由于 `=>` 的优先级低于大多数运算符，因此需要使用括号来避免 `callback || ()` 被解析为箭头函数的参数列表。

```js
callback = callback || (() => {});
```

示例
--

### 使用箭头函数

```js
// 空的箭头函数返回 undefined
const empty = () => {};

(() => "foobar")();
// 返回 "foobar"
// 这是一个立即执行函数表达式

const simple = (a) => (a > 15 ? 15 : a);
simple(16); // 15
simple(10); // 10

const max = (a, b) => (a > b ? a : b);

// 更方便进行数组的过滤、映射等工作
const arr = [5, 6, 13, 0, 1, 18, 23];

const sum = arr.reduce((a, b) => a + b);
// 66

const even = arr.filter((v) => v % 2 === 0);
// [6, 0, 18]

const double = arr.map((v) => v \* 2);
// [10, 12, 26, 0, 2, 36, 46]

// 更简明的 promise 链
promise
  .then((a) => {
    // …
  })
  .then((b) => {
    // …
  });

// 无参数箭头函数在视觉上容易分析
setTimeout(() => {
  console.log("我发生更早");
  setTimeout(() => {
    // 深层次代码
    console.log("我发生更晚");
  }, 1);
}, 1);
```

### 使用 call、bind 和 apply

`call()`、`apply()` 和 `bind()`方法与传统函数一样按照预期工作，因为我们为每个方法建立了作用域：

```js
const obj = {
  num: 100,
};

// 在 globalThis 上设置“num”，以显示它如何没有被使用到。
globalThis.num = 42;

// 对“this”进行操作的简单传统函数
const add = function (a, b, c) {
  return this.num + a + b + c;
};

console.log(add.call(obj, 1, 2, 3)); // 106
console.log(add.apply(obj, [1, 2, 3])); // 106
const boundAdd = add.bind(obj);
console.log(boundAdd(1, 2, 3)); // 106
```

对于箭头函数，由于我们的 `add` 函数基本上是在 `globalThis`（全局）作用域上创建的，因此它会假定 `this` 就是 `globalThis`。

```js
const obj = {
  num: 100,
};

// 在 globalThis 上设置“num”，以显示它是如何被接收到的。
globalThis.num = 42;

// 箭头函数
const add = (a, b, c) => this.num + a + b + c;

console.log(add.call(obj, 1, 2, 3)); // 48
console.log(add.apply(obj, [1, 2, 3])); // 48
const boundAdd = add.bind(obj);
console.log(boundAdd(1, 2, 3)); // 48
```

使用箭头函数的最大好处可能是在使用 `setTimeout()` 和 `EventTarget.prototype.addEventListener()` 等方法时，这些方法通常需要某种闭包、`call()`、`apply()` 或 `bind()`，以确保函数在适当的作用域中执行。

对于传统的函数表达式，类似这样的代码并不能像预期的那样工作：

```js
const obj = {
  count: 10,
  doSomethingLater() {
    setTimeout(function () {
      // 此函数在 window 作用域下执行
      this.count++;
      console.log(this.count);
    }, 300);
  },
};

obj.doSomethingLater(); // 输出“NaN”，因为“count”属性不在 window 作用域下。
```

有了箭头函数，`this` 作用域更容易被保留：

```js
const obj = {
  count: 10,
  doSomethingLater() {
    // 该方法语法将“this”与“obj”上下文绑定。
    setTimeout(() => {
      // 由于箭头函数没有自己的绑定，
      // 而 setTimeout（作为函数调用）本身也不创建绑定，
      // 因此使用了外部方法的“obj”上下文。
      this.count++;
      console.log(this.count);
    }, 300);
  },
};

obj.doSomethingLater(); // 输出 11
```

规范
--

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

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

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

参见
--

* 函数指南
* 函数参考
* `function`
* `function` 表达式
* 深入了解 ES6：箭头函数，载于 hacks.mozilla.org（2015）