剩余参数 - JavaScript

剩余参数
====

**剩余参数**语法允许我们将一个不定数量的参数表示为一个数组。

尝试一下
----

语法
--

```js
function(a, b, ...theArgs) {
  // ...
}
```

描述
--

如果函数的最后一个命名参数以`...`为前缀，则它将成为一个由剩余参数组成的真数组，其中从`0`（包括）到`theArgs.length`（排除）的元素由传递给函数的实际参数提供。

在上面的例子中，`theArgs`将收集该函数的第三个参数（因为第一个参数被映射到`a`，而第二个参数映射到`b`）和所有后续参数。

### 剩余参数和 `arguments`对象的区别

剩余参数和 `arguments`对象之间的区别主要有三个：

* 剩余参数只包含那些没有对应形参的实参，而 `arguments` 对象包含了传给函数的所有实参。
* `arguments`对象不是一个真正的数组，而剩余参数是真正的 `Array`实例，也就是说你能够在它上面直接使用所有的数组方法，比如 `sort`，`map`，`forEach`或`pop`。
* `arguments`对象还有一些附加的属性（如`callee`属性）。
### 从 arguments 到数组

引入了剩余参数来减少由参数引起的样板代码。
```

// Before rest parameters, "arguments" could be converted to a normal array using:

function f(a, b) {

  var normalArray = Array.prototype.slice.call(arguments);
  // -- or --
  var normalArray = [].slice.call(arguments);
  // -- or --
  var normalArray = Array.from(arguments);

  var first = normalArray.shift(); // OK, gives the first argument
  var first = arguments.shift(); // ERROR (arguments is not a normal array)

}

// Now we can easily gain access to a normal array using a rest parameter

function f(...args) {
  var normalArray = args;
  var first = normalArray.shift(); // OK, gives the first argument
}
```

### 解构剩余参数

剩余参数可以被解构，这意味着他们的数据可以被解包到不同的变量中。请参阅解构赋值。

```js
function f(...[a, b, c]) {
  return a + b + c;
}

f(1); // NaN (b and c are undefined)
f(1, 2, 3); // 6
f(1, 2, 3, 4); // 6 (the fourth parameter is not destructured)
```

示例
--

因为`theArgs`是个数组，所以你可以使用`length`属性得到剩余参数的个数：

```js
function fun1(...theArgs) {
  alert(theArgs.length);
}

fun1(); // 弹出 "0", 因为 theArgs 没有元素
fun1(5); // 弹出 "1", 因为 theArgs 只有一个元素
fun1(5, 6, 7); // 弹出 "3", 因为 theArgs 有三个元素
```

下例中，剩余参数包含了从第二个到最后的所有实参，然后用第一个实参依次乘以它们：

```js
function multiply(multiplier, ...theArgs) {
  return theArgs.map(function (element) {
    return multiplier \* element;
  });
}

var arr = multiply(2, 1, 2, 3);
console.log(arr); // [2, 4, 6]
```

下例演示了你可以在剩余参数上使用任意的数组方法，而`arguments`对象不可以：

```js
function sortRestArgs(...theArgs) {
  var sortedArgs = theArgs.sort();
  return sortedArgs;
}

alert(sortRestArgs(5, 3, 7, 1)); // 弹出 1,3,5,7

function sortArguments() {
  var sortedArgs = arguments.sort();
  return sortedArgs; // 不会执行到这里
}

alert(sortArguments(5, 3, 7, 1)); // 抛出 TypeError 异常:arguments.sort is not a function
```

为了在`arguments`对象上使用`Array`方法，它必须首先被转换为一个真正的数组。

```js
function sortArguments() {
  var args = Array.prototype.slice.call(arguments);
  var sortedArgs = args.sort();
  return sortedArgs;
}
console.log(sortArguments(5, 3, 7, 1)); // shows 1, 3, 5, 7
```

规范
--

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

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

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

参见
--

* 函数指南
* 函数参考
* 展开语法（`...`）
* 默认参数
* `arguments`
* `Array`


默认参数值 - JavaScript

默认参数值
=====

**函数默认参数**允许在没有值或 `undefined` 被传入时使用默认形参。

尝试一下
----

语法
--
```

function [name]([param1[ = defaultValue1 ][, ..., paramN[ = defaultValueN ]]]) {
    statements
}
```

描述
--

JavaScript 中函数的参数默认是 `undefined`。然而，在某些情况下可能需要设置一个不同的默认值。这是默认参数可以帮助的地方。

以前，一般设置默认参数的方法是在函数体测试参数是否为 `undefined`，如果是的话就设置为默认的值。

下面的例子中，如果在调用 `multiply` 时，参数 `b` 的值没有提供，那么它的值就为 `undefined`。如果直接执行 `a * b`，函数会返回 `NaN`。

```js
function multiply(a, b) {
  return a \* b;
}

multiply(5, 2); // 10
multiply(5); // NaN !
```

为了防止这种情况，第二行代码解决了这个问题，其中如果只使用一个参数调用`multiply`，则`b`设置为`1`：

```js
function multiply(a, b) {
  b = typeof b !== "undefined" ? b : 1;
  return a \* b;
}

multiply(5, 2); // 10
multiply(5); // 5
```

有了默认参数，我们不需要再在函数体内做不必要的检查。现在你可以在函数头将`b`的默认值置为`1`：

```js
function multiply(a, b = 1) {
  return a \* b;
}

multiply(5, 2); // 10
multiply(5); // 5
```

示例
--

### 传入 `undefined` vs 其他假值

在第二次调用中，即使第一个参数在调用时显式设置为`undefined`（虽然不是`null`或其他falsy值），但是`num`参数的值是默认值。

```js
function test(num = 1) {
  console.log(typeof num);
}

test(); // 'number' (num is set to 1)
test(undefined); // 'number' (num is set to 1 too)

// test with other falsy values:
test(""); // 'string' (num is set to '')
test(null); // 'object' (num is set to null)
```

### 调用时解析

在函数被调用时，参数默认值会被解析，所以不像 Python 中的例子，每次函数调用时都会创建一个新的参数对象。

```js
function append(value, array = []) {
  array.push(value);
  return array;
}

append(1); //[1]
append(2); //[2], not [1, 2]
```

这个规则对于函数和变量也是适用的。

```js
function callSomething(thing = something()) {
  return thing;
}

let numberOfTimesCalled = 0;
function something() {
  numberOfTimesCalled += 1;
  return numberOfTimesCalled;
}

callSomething(); // 1
callSomething(); // 2
```

### 默认参数可用于后面的默认参数

已经遇到的参数可用于以后的默认参数：

```js
function greet(name, greeting, message = greeting + " " + name) {
  return [name, greeting, message];
}

greet("David", "Hi"); // ["David", "Hi", "Hi David"]
greet("David", "Hi", "Happy Birthday!"); // ["David", "Hi", "Happy Birthday!"]
```

以下这个例子近似模拟了一些比较简单的情况，并说明了特殊情况是怎么被处理的。

```js
function go() {
  return ":P";
}

function withDefaults(
  a,
 b = 5,
 c = b,
 d = go(),
 e = this,
 f = arguments,
 g = this.value,
) {
  return [a, b, c, d, e, f, g];
}

function withoutDefaults(a, b, c, d, e, f, g) {
  switch (arguments.length) {
    case 0:
      a;
    case 1:
      b = 5;
    case 2:
      c = b;
    case 3:
      d = go();
    case 4:
      e = this;
    case 5:
      f = arguments;
    case 6:
      g = this.value;
    default:
  }
  return [a, b, c, d, e, f, g];
}

withDefaults.call({ value: "=^\_^=" });
// [undefined, 5, 5, ":P", {value:"=^\_^="}, arguments, "=^\_^="]

withoutDefaults.call({ value: "=^\_^=" });
// [undefined, 5, 5, ":P", {value:"=^\_^="}, arguments, "=^\_^="]
```

### 函数嵌套定义

在 Gecko 33 中引入。在函数体内的函数声明不能引用内部的默认参数，并且会在 SpiderMonkey 抛出一个`ReferenceError`（现在是 `TypeError`），参见 Firefox bug 1022967。默认参数总是会被首先执行，而在函数体内部的函数声明会在之后生效。

```js
// Doesn't work! Throws ReferenceError.
function f(a = go()) {
  function go() {
    return ":P";
  }
}
```

### 位于默认参数之后非默认参数

在 Gecko 26 之前，以下代码会造成`SyntaxError`错误。这已经在Firefox bug 1022967中修复，并在以后的版本中按预期方式工作。参数仍然设置为从左到右，覆盖默认参数，即使后面的参数没有默认值。

```js
function f(x = 1, y) {
  return [x, y];
}

f(); // [1, undefined]
f(2); // [2, undefined]
```

### 有默认值的解构参数

你可以通过解构赋值为参数赋值：

```js
function f([x, y] = [1, 2], { z: z } = { z: 3 }) {
  return x + y + z;
}

f(); // 6
```

规范
--

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

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

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

参见
--

* 函数指南
* 函数参考
* 剩余参数
* 空值合并运算符（`??`）


setter - JavaScript

setter
======

当尝试设置属性时，**`set`** 语法将对象属性绑定到要调用的函数。它还可以在类中应用。

尝试一下
----

语法
--

```js
{ set prop(val) { /\* … \*/ } }
{ set [expression](val) { /\* … \*/ } }
```

### 参数

`prop`

要绑定到给定函数的属性名。

`val`

用于保存尝试分配给`prop`的值的变量的一个别名。

表达式

从 ECMAScript 2015 开始，还可以使用一个计算属性名的表达式绑定到给定的函数。

描述
--

在 javascript 中，如果试着改变一个属性的值，那么对应的 setter 将被执行。setter 经常和 getter 连用以创建一个伪属性。不可能在具有真实值的属性上同时拥有一个 setter 器。

使用 set 语法时请注意：

* 它的标识符可以是数字或字符串；
* 它必须有一个明确的参数（详见 Incompatible ES5 change: literal getter and setter functions must now have exactly zero or one arguments）；
* 在对象字面量中，不能为一个已有真实值的变量使用 set，也不能为一个属性设置多个 set。
 ( `{ set x(v) { }, set x(v) { } }` 和 `{ x: ..., set x(v) { } }` 是不允许的 )
示例
--

### 在对象初始化时定义 setter

这将定义一个对象 `language` 的伪属性`current`，当`current`被分配一个值时，将使用该值更新`log`：

```js
const language = {
  set current(name) {
    this.log.push(name);
  },
  log: [],
};

language.current = "EN";
console.log(language.log); // ['EN']

language.current = "FA";
console.log(language.log); // ['EN', 'FA']
```

请注意，`current`属性是未定义的，访问它时将会返回 `undefined`。

### 用 `delete` 操作符移除一个 setter

我们可以使用`delete`操作符移除 setter。

```js
delete language.current;
```

### 使用 `defineProperty` 为当前对象定义 setter

我们可以随时使用 `Object.defineProperty()` 给一个已经存在的对象添加一个 setter。

```js
const o = { a: 0 };

Object.defineProperty(o, "b", {
  set: function (x) {
    this.a = x / 2;
  },
});

o.b = 10; // Runs the setter, which assigns 10 / 2 (5) to the 'a' property
console.log(o.a); // 5
```

### 使用计算属性名

```js
const expr = "foo";

const obj = {
  baz: "bar",
  set [expr](v) {
    this.baz = v;
  },
};

console.log(obj.baz); // "bar"
obj.foo = "baz"; // run the setter
console.log(obj.baz); // "baz"
```

规范
--

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

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

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

参见
--

* getter
* `delete`
* `Object.defineProperty()`
* `Object.prototype.__defineGetter__()`
* `Object.prototype.__defineSetter__()`
* JavaScript 指南中定义 Getter 和 Setter 一文
* 不兼容的 ES5 变化：字面量的 getter 和 setter 函数现在必须正好有零或一个参数


arguments[@@iterator]() - JavaScript

arguments[@@iterator]()
=======================

**`@@iterator`** 属性的初始值是和 `Array.prototype.values` 属性的初始值相同的对象。

语法
--
```

arguments[Symbol.iterator]()
```

示例
--

### 使用`for...of`循环的迭代

```js
function f() {
  // 你的浏览器必须支持 for..of 循环
  // 以及 for 循环中的 let 区域变量
  for (let letter of arguments) {
    console.log(letter);
  }
}
f("w", "y", "k", "o", "p");
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-createunmappedargumentsobject |
| ECMAScript Language Specification # sec-createmappedargumentsobject |

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

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

更多
--

* `Array.prototype.values()`


arguments.callee - JavaScript

arguments.callee
================

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

**备注：** 在严格模式下访问 `arguments.callee` 会抛出 `TypeError`。若一个函数必须引用自身，请为函数表达式命名，或使用函数声明。

**`arguments.callee`** 数据属性包含 arguments 所属的当前正在执行的函数。

值
-

对当前正在执行的函数的引用。

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

**备注：** `callee` 是仅存在于具有简单参数的非严格函数（在这种情况下 `arguments` 对象也是自动同步的）的数据属性。否则，它是一个访问器属性，其 getter 和 setter 都会抛出 `TypeError`。

描述
--

`callee` 是 `arguments` 对象的一个属性。它可以用于引用某个函数的函数体内当前正在执行的函数。这在函数的名称未知时很有用，例如在没有名称的函数表达式（也称为“匿名函数”）内。

（以下内容大部分改编自 olliej 在 Stack Overflow 上的回答）

早期版本的 JavaScript 不允许使用具名函数表达式，出于这样的原因，你不能创建递归函数表达式。

例如，以下语法是有效的：

```js
function factorial(n) {
  return n <= 1 ? 1 : factorial(n - 1) \* n;
}

[1, 2, 3, 4, 5].map(factorial);
```

但是：

```js
[1, 2, 3, 4, 5].map(function (n) {
  return n <= 1 ? 1 : /\* 这里该怎么写？\*/ (n - 1) \* n;
});
```

这个就不行。为了解决这个问题，`arguments.callee` 被添加了进来。然后你可以这么做：

```js
[1, 2, 3, 4, 5].map(function (n) {
  return n <= 1 ? 1 : arguments.callee(n - 1) \* n;
});
```

然而，`arguments.callee` 的设计存在很多问题。第一个问题是递归调用会得到不同的 `this` 值。例如：

```js
const global = this;

const sillyFunction = function (recursed) {
  if (this !== global) {
    console.log("this 是", this);
  } else {
    console.log("this 是 global");
  }

  if (!recursed) {
    return arguments.callee(true);
  }
};

sillyFunction();
// this 是 global
// this 是 [object Arguments]
```

此外，`arguments.callee` 的引用使得在一般情况下无法实现内联和尾递归优化。（你可以通过追踪个别的例子等方法来实现它，但即使是最好的代码也是次优选项，因为做了本来是不需要的检查。）

ECMAScript 3 通过允许具名函数表达式解决了这些问题。例如：

```js
[1, 2, 3, 4, 5].map(function factorial(n) {
  return n <= 1 ? 1 : factorial(n - 1) \* n;
});
```

这有很多好处：

* 该函数可以像代码内部的任何其他函数一样被调用
* 它不会在外部作用域中创建变量（除了 IE 8 及以下）
* 它具有比访问 arguments 对象更好的性能

严格模式禁用了其他泄露堆栈信息的属性，比如函数的 `caller` 属性。这是因为检查调用堆栈有一个重大影响：它使大量的优化变得不再可能，或者变得更加困难。比如，如果你不能保证一个函数 `f` 不会调用一个未知函数，那就无法内联优化 `f`。

```js
function f(a, b, c, d, e) {
  return a ? b \* c : d \* e;
}
```

如果 JavaScript 解释器不能保证所有提供的参数在被调用时都是数字，那么它需要在内联代码之前插入对所有参数的检查，否则无法内联该函数。这意味着任何可能微不足道的内联调用的地方都会积累大量的守卫。现在，在这个特殊例子中，智能的解释器应该能重排检查而使其更优，且不会检查任何没有使用到的值。然而在许多的情况下这是不可能的，因而无法进行内联优化。

示例
--

### 在匿名递归函数中使用 arguments.callee

递归函数必须能够引用它本身。通常，函数通过自己的名字调用自己。然而，匿名函数（通过函数表达式或者 `Function` 构造函数创建）没有名称。因此如果没有可访问的变量指向该函数，唯一能引用它自身的方式就是通过 `arguments.callee`。

下面的示例定义了一个函数，该函数又定义并返回了一个阶乘函数。该示例并不实用，且几乎所有情况下都能用具名函数表达式实现同样结果。

```js
function create() {
  return function (n) {
    if (n <= 1) {
      return 1;
    }
    return n \* arguments.callee(n - 1);
  };
}

const result = create()(5); // 返回 120（5 \* 4 \* 3 \* 2 \* 1）
```

### 使用 Y 组合子的匿名函数递归

虽然函数表达式现在可以被命名，但箭头函数始终是匿名的，这意味着它们无法在没有被赋值给变量的情况下引用自身。幸运的是，在 Lambda 演算中有一个非常好的解决方案，它允许一个函数既是匿名的又是自引用的。这个技术被称为 Y 组合子。在这里我们不会解释它是*如何工作*的，而仅介绍*如何使用*。

```js
// Y 组合子：工具函数！
const Y = (hof) => ((x) => x(x))((x) => hof((y) => x(x)(y)));

console.log(
  [1, 2, 3, 4, 5].map(
    // 将高阶函数包装在 Y 组合子中
    // “factorial”不是函数的名称：它是作为参数引入的
    Y((factorial) => (n) => (n <= 1 ? 1 : factorial(n - 1) \* n)),
  ),
);
// [ 1, 2, 6, 24, 120 ]
```

**备注：** 此方法为每一次迭代创建一个新的闭包，这可能会显著增加内存的使用量。这里只是为了演示这种可能性，但应该在生产环境中避免。请改用临时变量或具名函数表达式。

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-arguments-exotic-objects |

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

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

参见
--

* 函数指南
* 函数
* `arguments`
* `Function.prototype.caller`


arguments.length - JavaScript

arguments.length
================

本次函数调用时传入函数的实参数量。

Syntax
------
```

arguments.length
```

描述
--

arguments.length 表示的是实际上向函数传入了多少个参数，这个数字可以比形参数量大，也可以比形参数量小 (形参数量的值可以通过Function.length获取到).

示例
--

### 示例：使用`arguments.length`

这个例中，我们定义了一个可以相加任意个数字的函数。

```js
function adder(base /\*, n2, ... \*/) {
  base = Number(base);
  for (var i = 0; i < arguments.length; i++) {
    base += Number(arguments[i]);
  }
  return base;
}
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-arguments-exotic-objects |

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

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

参见
--

* Function.length


按位或（

按位或（|）
======

**按位或**（**`|`**）运算符在其中一个或两个操作数对应的二进制位为 `1` 时，该位的结果值为 `1`。

尝试一下
----

语法
--

```js
a | b
```

描述
--

操作数被转换为 32 位整数并由一系列二进制位（0 和 1）表示。超过 32 位的数字会丢弃其最高有效位。例如，以下超过 32 位的整数将被转换为 32 位整数：
```

Before: 11100110111110100000000000000110000000000001
After:              10100000000000000110000000000001
```

第一个操作数中的每个位都与第二个操作数中的相应位配对：*第一位*到*第一位*，*第二位*到*第二位*，依此类推。

运算符应用于每一对位，结果按位构造。

或运算的真值表为：

| a | b | a OR b |
| --- | --- | --- |
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 1 |
```

     9 (base 10) = 00000000000000000000000000001001 (base 2)
    14 (base 10) = 00000000000000000000000000001110 (base 2)
                   --------------------------------
14 | 9 (base 10) = 00000000000000000000000000001111 (base 2) = 15 (base 10)
```

将任何数字 `x` 与 `0` 进行按位或运算得到 `x`。

示例
--

### 使用按位或

```js
// 9 (00000000000000000000000000001001)
// 14 (00000000000000000000000000001110)

14 | 9;
// 15 (00000000000000000000000000001111)
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # prod-BitwiseORExpression |

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

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

参见
--

* JS 指南中的位运算符
* 按位或赋值运算符


无符号右移（>>>） - JavaScript

无符号右移（>>>）
==========

**无符号右移运算符（`>>>`）**（零填充右移）将左操作数计算为无符号数，并将该数字的二进制表示形式移位为右操作数指定的位数，取模 32。向右移动的多余位将被丢弃，零位从左移入。其符号位变为 `0`，因此结果始终为非负数。与其他按位运算符不同，零填充右移返回一个无符号 32 位整数。

尝试一下
----

语法
--

```js
a >>> b
```

描述
--

该运算符将第一个操作数向右移动指定的位数。向右移动的多余位将被丢弃。零位从左侧移入。其符号位变为 `0`，因此其表示的结果始终为非负数。与其他按位运算符不同，零填充右移返回无符号 32 位整数。

以十进制（以 10 为基数）数字 `9` 和 `-9` 的 32 位二进制表示为例：
```

     9 (base 10): 00000000000000000000000000001001 (base 2)
    -9 (base 10): 11111111111111111111111111110111 (base 2)
```

请注意，负十进制（以 10 为基数）数字 `-9` 的二进制表示形式是正十进制（以 10 为基数）数字 `9` 的二进制补码。也就是说，它是通过反转 `00000000000000000000000000001001` 的所有位并添加 `1` 来计算的。

在这两种情况下，二进制数的符号都由其最左边的位给出：对于正十进制数 `9`，二进制表示的最左边位是 `0`，对于负十进制数 `-9`，二进制表示的最左边位是 `1`。

对于正数 `9`，零填充右移和符号传播右移产生相同的结果：`9 >>> 2` 产生 `2`，与 `9 >> 2` 相同：
```

      9 (base 10): 00000000000000000000000000001001 (base 2)
                   --------------------------------
9 >>  2 (base 10): 00000000000000000000000000000010 (base 2) = 2 (base 10)
9 >>> 2 (base 10): 00000000000000000000000000000010 (base 2) = 2 (base 10)
```

请注意，最右边的两个位 `01` 被移出，两个零从左边移入。

但是，请注意 `-9` 会发生什么情况：`-9 >> 2`（符号传播右移）产生 `-3`，但 `-9 >>> 2`（零填充右移）产生 1073741821：
```

      -9 (base 10): 11111111111111111111111111110111 (base 2)
                    --------------------------------
-9 >>  2 (base 10): 11111111111111111111111111111101 (base 2) = -3 (base 10)
-9 >>> 2 (base 10): 00111111111111111111111111111101 (base 2) = 1073741821 (base 10)
```

请注意最右边的两个位 `11` 是如何移出的。对于 `-9 >> 2`（符号传播右移），最左边的 `1` 位的两个副本已从左侧移入，这保留了负号。另一方面，对于 `-9 >>> 2`（零填充右移），零从左移入，因此不会保留数字的负号，结果是一个（大）正数。

左操作数将被转换为无符号 32 位整数，这意味着浮点数将被截断，而超出 32 位边界的数字将溢出/下溢。

右操作数将转换为无符号的 32 位整数，然后取模 32，因此实际移位偏移量将始终是介于 0 和 31 之间的正整数（包括 0 和 31）。例如，`100 >>> 32` 与 `100 >>> 0` 相同（并产生 `100`），因为 32 模 32 是 0。

示例
--

### 使用无符号右移

```js
9 >>> 2; // 2
-9 >>> 2; // 1073741821
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-unsigned-right-shift-operator |

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

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

参见
--

* JS 指南：位运算符
* 无符号右移赋值运算符


加法（+） - JavaScript

加法（+）
=====

**加法**（**`+`**）运算符计算数字操作数或字符串连接的总和。

尝试一下
----

语法
--

```js
x + y
```

描述
--

加法运算符（`+`）为两种不同的运算重载：数字加法和字符串连接。在求值时，它首先将两个操作数强制转换为基本类型。然后，检查两个操作数的类型：

* 如果有一方是字符串，另一方则会被转换为字符串，并且它们连接起来。
* 如果双方都是 BigInt，则执行 BigInt 加法。如果一方是 BigInt 而另一方不是，会抛出 `TypeError`。
* 否则，双方都会被转换为数字，执行数字加法。

字符串连接经常被认为等价于模板字符串或者 `String.prototype.concat()`，但并非如此。加法强制将表达式转为*基本类型*，它优先调用 `valueOf()`；另一方面，模板字符串和 `concat()` 则强制将表达式转为*字符串*，它们优先调用 `toString()`。如果表达式有 `@@toPrimitive` 方法，字符串连接时会用 `"default"` 作为 `hint` 调用它，然而模板字符串则用 `"string"`。这对于具有不同的字符串和原始值表现的对象来说很重要——例如 Temporal，它的 `valueOf()` 方法会抛出错误。

```js
const t = Temporal.Now.instant();
"" + t; // 抛出 TypeError
`${t}`; // '2022-07-31T04:48:56.113918308Z'
"".concat(t); // '2022-07-31T04:48:56.113918308Z'
```

建议不要使用 `"" + x` 来执行字符串强制转换。

示例
--

### 数字加法

```js
// Number + Number -> addition
1 + 2; // 3

// Boolean + Number -> addition
true + 1; // 2

// Boolean + Boolean -> addition
false + false; // 0
```

### 字符串连接

```js
// String + String -> concatenation
"foo" + "bar"; // "foobar"

// Number + String -> concatenation
5 + "foo"; // "5foo"

// String + Boolean -> concatenation
"foo" + false; // "foofalse"

// String + Number -> concatenation
"2" + 2; // "22"
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-addition-operator-plus |

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

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

参见
--

* 减法运算符
* 除法运算符
* 乘法运算符
* 取余运算符
* 求幂运算符
* 自增运算符
* 自减运算符
* 一元减运算符
* 一元加运算符


无符号右移赋值（>>>=） - JavaScript

无符号右移赋值（>>>=）
=============

**无符号右移赋值**（**`>>>=`**）运算符向右移动移动指定（二进制）位数，并将结果赋值给变量。

尝试一下
----

语法
--

```js
x >>>= y // x = x >>> y
```

示例
--

### 使用无符号右移位赋值

```js
let a = 5; // (00000000000000000000000000000101)
a >>>= 2; // 1 (00000000000000000000000000000001)

let b = -5; // (-00000000000000000000000000000101)
b >>>= 2; // 1073741822 (00111111111111111111111111111110)
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-assignment-operators |

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

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

参见
--

* JS 指南中的赋值运算符
* 无符号右移位运算符


小于（<） - JavaScript

小于（<）
=====

**小于**（`<`）运算符在左操作数比右操作数小时返回 `true`，否则返回 `false`。

尝试一下
----

语法
--

```js
x < y
```

描述
--

操作数经过多轮强制比较，可以总结为以下几点：

首先，通过依次调用其 `[@@toPrimitive]()`（以 `"number"` 作为提示）、`valueOf()` 和 `toString()` 方法，将对象转换为原始类型。左边的操作数总是在右边的操作数之前被强制转换。请注意，尽管 `[@@toPrimitive]()` 被调用时带有 `"number"` 的提示（意味着有一点倾向于将对象变成数字），但返回值并没有转换为数字，因为字符串仍然被特别处理。

* 如果两个值都是字符串，则根据它们所包含的 Unicode 码位的值，将它们作为字符串进行比较。
* 否则，JavaScript 会尝试将非数值类型转化为数值类型：
    + 布尔值 `true` 和 `false` 分别转化为 1 和 0。
    + `null` 转化为 0。
    + `undefined` 转化为 `NaN`。
    + 字符串根据其包含的值进行转换，如果不包含数字值，则转换为 `NaN`。
* 如果任意一个值为 `NaN`，则运算符返回 `false`。
* 否则，这些值将作为数值进行比较。BigInt 和数值可以一起比较。

其他运算符，包括 `>`、`>=` 和 `<=` 使用与 `<` 相同的算法。有两种情况，所有四个运算符都返回 `false`：

* 如果其中一个操作数被转换为 BigInt，而另一个被转换为无法转换为 BigInt 值的字符串（当传递给 `BigInt()`时，它会抛出语法错误 (en-US)）。
* 如果其中一个操作数被转化为 `NaN`，如不能转化为数字的字符串，或 `undefined`。

对于所有其他情况，这四种运算符有以下关系：

```js
x < y === !(x >= y);
x <= y === !(x > y);
x > y === y < x;
x >= y === y <= x;
```

**备注：** `<` 和 `>` 之间一个明显区别是强制的顺序，特别是当强制转化为原始值有副作用时。所有的比较操作符都是先强制转化左操作数再强制转化右操作数。

示例
--

### 字符串之间的比较

```js
"a" < "b"; // true
"a" < "a"; // false
"a" < "3"; // false
```

### 字符串与数值比较

```js
"5" < 3; // false
"3" < 3; // false
"3" < 5; // true

"hello" < 5; // false
5 < "hello"; // false

"5" < 3n; // false
"3" < 5n; // true
```

### 数值之间的比较

```js
5 < 3; // false
3 < 3; // false
3 < 5; // true
```

### 数值与 BigInt 之间的比较

```js
5n < 3; // false
3 < 5n; // true
```

### 比较布尔值、null、undefined 和 NaN

```js
true < false; // false
false < true; // true

0 < true; // true
true < 1; // false

null < 0; // false
null < 1; // true

undefined < 3; // false
3 < undefined; // false

3 < NaN; // false
NaN < 3; // false
```

### 含有副作用的比较

比较总是将其操作数强制转化为原始值。这意味着同一个对象在一个比较表达式中可能最终会有不同的值。例如，你可能有两个既大于又小于对方的值。

```js
class Mystery {
  static #coercionCount = -1;
  valueOf() {
    Mystery.#coercionCount++;
    // 左侧操作数先强制转化，所以对右操作数会先返回 0 再返回 1
    return Mystery.#coercionCount % 2;
  }
}
const l = new Mystery();
const r = new Mystery();
console.log(l < r && r < l);
// true
```

**警告：** 这可能是混乱的根源。如果你的对象提供了自定义的原始值转换逻辑，请确保它是*幂等的*：多次强制转化应该返回相同的值。

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-relational-operators |

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

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

参见
--

* 大于运算符
* 大于等于运算符
* 小于等于运算符


取余赋值（%=） - JavaScript

取余赋值（%=）
========

**取余赋值**（**`%=`**）运算符将变量除以右操作数的值，并将余数赋值给该变量。

尝试一下
----

语法
--

```js
x %= y // x = x % y
```

示例
--

### 使用取余赋值

```js
let bar = 5;

bar %= 2; // 1
bar %= "foo"; // NaN
bar %= 0; // NaN
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-assignment-operators |

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

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

参见
--

* JS 指南中的赋值运算符
* 取余运算符


delete 运算符 - JavaScript

delete 运算符
==========

**`delete`** 运算符用于删除对象的一个属性；如果该属性的值是一个对象，并且没有更多对该对象的引用，该属性所持有的对象最终会自动释放。

尝试一下
----

语法
--

```js
delete object.property
delete object[property]
```

**备注：** 该语法允许在 `delete` 运算符之后使用多种类型的表达式，但只有上述形式才能产生有意义的行为。

### 参数

`object`

对象的名称，或计算结果为对象的表达式。

`property`

要删除的属性。

### 返回值

对于大多数情况都是 `true`；如果属性是一个自身不可配置的属性，在这种情况下，非严格模式返回 `false`。

### 异常

`TypeError`

如果属性是自身不可配置的属性且处于严格模式中，则会抛出该异常。

`ReferenceError`

当 `object` 是 `super` 时抛出。

描述
--

`delete` 运算符与其他像 `typeof` 这样的一元运算符具有相同的优先级。因此，它接受任何由更高优先级的运算符形成的表达式。然而，在严格模式下，以下形式会导致早期语法错误：

```js
delete identifier;
delete object.#privateProperty;
```

因为类自动处于严格模式，而私有属性只能在类体内合法引用，这意味着私有属性永远不能被删除。虽然 `delete identifier` 在 `identifier` 指的是全局对象的可配置属性时可能有效，但是你应该避免这种形式，而是用 `globalThis` 作为前缀。

虽然其他表达式是可以接受的，但是它们并不导致有意义的行为：

```js
delete console.log(1);
// 输出 1，返回 true，但是没有删除任何东西
```

`delete` 运算符从一个对象中删除一个给定的属性。在成功删除时，它将返回 `true`，否则将返回 `false`。不像一般人认为的那样（也许是由于其他编程语言，如 C++ 中的 delete），`delete` 操作符与直接释放内存**没有**关系。内存管理是通过破坏引用间接完成的。更多细节请参见内存管理页面。

但是，以下情况需要重点考虑：

* 如果你试图删除的属性不存在，那么 `delete` 将不会起任何作用，但仍会返回 `true`。
* `delete` 只影响自身属性。如果对象的原型链上有一个与待删除属性同名的属性，那么删除属性之后，对象会使用原型链上的那个属性。
* 不可配置的属性不能被移除。这意味着像 `Math`、`Array`、`Object` 这些内置对象的属性以及使用 `Object.defineProperty()` 方法设置为不可配置的属性不能被删除。
* 删除包括函数参数内的变量永远不会奏效。`delete variable` 会在严格模式下抛出 `SyntaxError` 错误，非严格模式下不会有任何效果。
    + 任何使用 `var` 声明的属性不能从全局作用域或函数的作用域中删除，因为即使它们可能附加到全局对象上，它们也是不可配置的。
    + 任何使用 `let` 或 `const` 声明的属性不能够从它被声明的作用域中删除，因为它们没有附加到任何对象上。
示例
--

### 使用 delete

**备注：** 以下示例使用了仅非严格模式下的功能，如隐式创建全局变量和删除标识符，这在严格模式下是禁止的。

```js
// 在全局作用域创建 empCount 属性
// 因为我们使用了 var，它会标记为不可配置
var empCount = 43;

// 在全局作用域创建 adminName 属性
// 因为没有使用 var，它会标记为可配置
adminName = "xyz";

EmployeeDetails = {
  name: "xyz",
  age: 5,
  designation: "Developer",
};

// delete 可用于删除对象的属性
delete EmployeeDetails.name; // 返回 true

// 甚至属性不存在，它也会返回 "true"
delete EmployeeDetails.salary; // 返回 true

// EmployeeDetails 是全局作用域的一个属性。
delete EmployeeDetails; // 返回 true

// 相反，empCount 是不可配置的，
// 因为创建它时使用了 var。
delete empCount; // 返回 false

// adminName 是全局作用域的一个属性。
// 因为它不是用 var 创建的，所以可以删除。
// 因此，它是可配置的。
delete adminName; // 返回 true

// delete 对内建静态属性不起作用
// 这些属性是不可配置的
delete Math.PI; // 返回 false

function f() {
  var z = 44;

  // delete 对局部变量名不起作用
  delete z; // 返回 false
}
```

### delete 和原型链

在下面的示例中，我们删除一个对象的自有属性，而原型链上具有相同名称的属性可用：

```js
function Foo() {
  this.bar = 10;
}

Foo.prototype.bar = 42;

var foo = new Foo();

// foo.bar 指代了自身属性
console.log(foo.bar); // 10

// 删除 foo 对象的自身属性
delete foo.bar; // 返回 true

// foo.bar 仍然在原型链上可用。
console.log(foo.bar); //42

// 从原型上删除属性
delete Foo.prototype.bar; // 返回 true

// 由于已删除“bar”属性，因此不能再从 Foo 继承它。
console.log(foo.bar); //undefined
```

### 删除数组元素

当你删除一个数组元素时，数组的长度（`length`）不受影响。即便你删除了数组的最后一个元素也是如此。

当用 `delete` 运算符删除一个数组元素时，被删除的元素已经不再属于该数组。下面的例子中用 `delete` 删除了 `trees[3]`。

```js
const trees = ["redwood", "bay", "cedar", "oak", "maple"];
delete trees[3];
console.log(3 in trees); // false
```

以上操作创建了一个稀疏数组，如果你想让一个数组元素继续存在，但是其值是 `undefined`，那么可以将 `undefined` 赋值给这个元素而不是使用 `delete`。下面的例子中，`trees[3]` 被赋值为 `undefined`，但该数组元素仍然存在。

```js
const trees = ["redwood", "bay", "cedar", "oak", "maple"];
trees[3] = undefined;
console.log(3 in trees); // true
```

如果你想通过改变数组的内容来移除一个数组元素，请使用 `splice()` 方法。在下面的例子中，通过使用 `splice()`，将 `trees[3]` 从数组中移除。

```js
const trees = ["redwood", "bay", "cedar", "oak", "maple"];
trees.splice(3, 1);
console.log(trees); // ["redwood", "bay", "cedar", "maple"]
```

### 删除不可配置属性

当一个属性被标记为不可配置时，`delete` 不会有任何效果，并将返回 `false`。在严格模式下，会抛出 `TypeError`。

```js
const Employee = {};
Object.defineProperty(Employee, "name", { configurable: false });

console.log(delete Employee.name); // 返回 false
```

`var` 会创建不可配置的属性，它不能用 `delete` 运算符删除。

```js
// 由于“nameOther”是使用 var 关键字声明的，
// 它被标记为不可配置的
var nameOther = "XYZ";

// 我们可以通过以下代码访问这个全局属性：
Object.getOwnPropertyDescriptor(globalThis, "nameOther");
// {
// value: "XYZ",
// writable: true,
// enumerable: true,
// configurable: false
// }

delete globalThis.nameOther; // 返回 false
```

在严格模式下，会抛出异常。

### 删除全局属性

如果一个全局属性是可配置的（例如，通过直接的属性赋值），它可以被删除，随后将它们作为全局变量的引用将产生 `ReferenceError`。

```js
globalThis.globalVar = 1;
console.log(globalVar); // 1
// 在非严格模式下，也可以使用 `delete globalVar`
delete globalThis.globalVar;
console.log(globalVar); // ReferenceError: globalVar is not defined
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-delete-operator |

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

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

参见
--

* 深入分析 delete
* `Reflect.deleteProperty()`
* `Map.prototype.delete()`


function* 表达式 - JavaScript

function\* 表达式
==============

\*\*`function*`\*\*关键字可以在表达式内部定义一个生成器函数。

尝试一下
----

语法
--
```

function* [name]([param1[, param2[, ..., paramN]]]) {
   statements
}
```

### 参数

`name`

函数名。在声明*匿名函数*时可以省略。函数名称只是函数体中的一个本地变量。

`paramN`

传入函数的一个参数名。一个函数最多有 255 个参数。

`statements`

函数体。

描述
--

`function*`表达式和`function* 声明`比较相似，并具有几乎相同的语法。`function*`表达式和`function*`声明之间主要区别就是函数名，即在创建匿名函数时，`function*`表达式可以省略函数名。阅读`函数`章节了解更多信息。

示例
--

下面的示例定义了一个未命名的生成器函数并把它赋值给`x`。函数产出它的传入参数的平方：

```js
var x = function\* (y) {
  yield y \* y;
};
```

规范
--

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

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

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

参见
--

* `function*`
* `GeneratorFunction`
* 迭代器协议
* `yield`
* `yield*`


this - JavaScript

this
====

与其他语言相比，**函数的 `this` 关键字**在 JavaScript 中的表现略有不同，此外，在严格模式和非严格模式之间也会有一些差别。

在绝大多数情况下，函数的调用方式决定了 `this` 的值（运行时绑定）。`this` 不能在执行期间被赋值，并且在每次函数被调用时 `this` 的值也可能会不同。ES5 引入了 bind 方法来设置函数的 `this` 值，而不用考虑函数如何被调用的。ES2015 引入了箭头函数，箭头函数不提供自身的 this 绑定（`this` 的值将保持为闭合词法上下文的值）。

尝试一下
----

语法
--
```

this
```

### 值

在非严格模式下，`this` 总是指向一个对象，在严格模式下可以是任意值。有关如何确定该值的更多信息，请参阅下面的描述。

描述
--

`this` 的值取决于它出现的上下文：函数、类或全局。

### 函数上下文

在函数内部，`this` 的值取决于函数如何被调用。可以将 `this` 看作是函数的一个隐藏参数（就像函数定义中声明的参数一样），`this` 是语言在函数体被执行时为你创建的绑定。

对于典型的函数，`this` 的值是函数被访问的对象。换句话说，如果函数调用的形式是 `obj.f()`，那么 `this` 就指向 `obj`。例如：

```js
function getThis() {
  return this;
}

const obj1 = { name: "obj1" };
const obj2 = { name: "obj2" };

obj1.getThis = getThis;
obj2.getThis = getThis;

console.log(obj1.getThis()); // { name: 'obj1', getThis: [Function: getThis] }
console.log(obj2.getThis()); // { name: 'obj2', getThis: [Function: getThis] }
```

注意，虽然函数是相同的，但是根据其调用的方式，`this` 的值是不同的。这与函数参数的工作方式类似。

`this` 的值不是拥有此函数作为自己属性的对象，而是用于调用此函数的对象。你可以通过调用对象在原型链中的方法来证明这一点。

```js
const obj3 = {
  \_\_proto\_\_: obj1,
  name: "obj3",
};

console.log(obj3.getThis()); // { name: 'obj3' }
```

`this` 的值总是根据调用函数的方式而改变，即使函数是在创建对象时定义的：

```js
const obj4 = {
  name: "obj4",
  getThis() {
    return this;
  },
};

const obj5 = { name: "obj5" };

obj5.getThis = obj4.getThis;
console.log(obj5.getThis()); // { name: 'obj5', getThis: [Function: getThis] }
```

如果方法被访问的值是一个原始值，`this` 也将是一个原始值——但只有当函数处于严格模式下会如此。

```js
function getThisStrict() {
  "use strict"; // 进入严格模式
  return this;
}

// 仅用于演示——你不应该改变内置的原型对象
Number.prototype.getThisStrict = getThisStrict;
console.log(typeof (1).getThisStrict()); // "number"
```

如果函数在没有被任何东西访问的情况下被调用，`this` 将是 `undefined`——但只有在函数处于严格模式下会如此。

```js
console.log(typeof getThisStrict()); // "undefined"
```

在非严格模式下，一个特殊的过程称为 `this` 替换确保 `this` 的值总是一个对象。这意味着：

* 如果一个函数被调用时 `this` 被设置为 `undefined` 或 `null`，`this` 会被替换为 `globalThis`。
* 如果函数被调用时 `this` 被设置为一个原始值，`this` 会被替换为原始值的包装对象。

```js
function getThis() {
  return this;
}

// 仅用于演示——你不应该修改内置的原型对象
Number.prototype.getThis = getThis;
console.log(typeof (1).getThis()); // "object"
console.log(getThis() === globalThis); // true
```

在典型的函数调用中，`this` 是通过函数的前缀（点之前的部分）隐式传递的，就像一个参数。你也可以使用 `Function.prototype.call()`、`Function.prototype.apply()` 或 `Reflect.apply()` 方法显式设置 `this` 的值。使用 `Function.prototype.bind()`，你可以创建一个新的函数，无论函数如何被调用，其 `this` 的值都不会改变。当使用这些方法时，如果函数是在非严格模式下，上述 `this` 替换规则仍然适用。

#### 回调

当一个函数作为回调函数传递时，`this` 的值取决于如何调用回调，这由 API 的实现者决定。回调函数通常以 `undefined` 作为 `this` 的值被调用（直接调用，而不附加到任何对象上），这意味着如果函数是在非严格模式，`this` 的值会是全局对象（`globalThis`）。这在迭代数组方法、`Promise()` 构造函数等例子中都是适用的。

```js
function logThis() {
  "use strict";
  console.log(this);
}

[1, 2, 3].forEach(logThis); // undefined、undefined、undefined
```

一些 API 允许你为回调函数的调用设置一个 `this` 值。例如，所有的迭代数组方法和相关的方法，如`Set.prototype.forEach()`，都接受一个可选的 `thisArg` 参数。

```js
[1, 2, 3].forEach(logThis, { name: "obj" });
// { name: 'obj' }, { name: 'obj' }, { name: 'obj' }
```

偶尔，回调函数会以一个非 `undefined` 的 `this` 值被调用。例如，`JSON.parse()` 的 `reviver` 参数和 `JSON.stringify()` 的 `replacer` 参数都会把 `this` 设置为正在被解析/序列化的属性所属的对象。

#### 箭头函数

在箭头函数中，`this` 保留了闭合词法上下文的 `this` 值。换句话说，当对箭头函数求值时，语言不会创建一个新的 `this` 绑定。

例如，在全局代码中，无论是否在严格模式下，由于全局上下文绑定，`this` 值总是 `globalThis`。

```js
const globalObject = this;
const foo = () => this;
console.log(foo() === globalObject); // true
```

箭头函数在其周围的作用域上创建一个 `this` 值的闭包，这意味着箭头函数的行为就像它们是“自动绑定”的——无论如何调用，`this` 都绑定到函数创建时的值（在上面的例子中，是全局对象）。在其他函数内部创建的箭头函数也是如此：它们的 `this` 值保持为闭合词法上下文的 `this`。参见下面的例子。

此外，当使用 `call()`、`bind()` 或 `apply()` 调用箭头函数时，`thisArg` 参数会被忽略。不过，你仍然可以使用这些方法传递其他参数。

```js
const obj = { name: "obj" };

// 尝试使用 call 设置 this
console.log(foo.call(obj) === globalObject); // true

// 尝试使用 bind 设置 this
const boundFoo = foo.bind(obj);
console.log(boundFoo() === globalObject); // true
```

#### 构造函数

当一个函数被用作构造函数（使用 `new` 关键字）时，无论构造函数是在哪个对象上被访问的，其 `this` 都会被绑定到正在构造的新对象上。除非构造函数返回另一个非原始值，不然 `this` 的值会成为 `new` 表达式的值。

```js
function C() {
  this.a = 37;
}

let o = new C();
console.log(o.a); // 37

function C2() {
  this.a = 37;
  return { a: 38 };
}

o = new C2();
console.log(o.a); // 38
```

在第二个例子（`C2`）中，因为在构造过程中返回了一个对象，`this` 被绑定的新对象被丢弃。（这基本上使得语句 `this.a = 37;` 成为了死代码。它并不完全是死代码，因为它被执行了，但是它可以被消除而不产生任何外部效果。）

#### super

当一个函数以 `super.method()` 的形式被调用时，`method` 函数内的 `this` 与 `super.method()` 调用周围的 `this` 值相同，通常不等于 `super` 所指向的对象。这是因为 `super.method` 不是像上面的对象成员访问——它是一种特殊的语法，有不同的绑定规则。有关示例，请参见 `super` 参考。

### 类上下文

一个类可以被分为两个上下文：静态和实例。构造函数、方法和实例字段初始化器（公有或私有）属于实例上下文。静态方法、静态字段初始化器和静态初始化块 (en-US)属于静态上下文。`this` 值在每个上下文中都是不同的。

类构造函数总是通过 `new` 调用，所以它们的行为与构造函数相同：`this` 值是正在创建的新实例。类方法的行为像对象字面量中的方法——`this` 值是方法被访问的对象。如果方法没有转移到另一个对象，`this` 通常是类的一个实例。

静态方法不是 `this` 的属性。它们是类本身的属性。因此，它们通常在类上访问，`this` 是类（或子类）的值。静态初始化块也是在 `this` 设置为当前类的情况下进行求值的。

字段初始化器也在类的上下文中执行。实例字段是在 `this` 被设置为正在构造的实例的情况下被初始化的。静态字段是在 `this` 被设置为当前类的情况下被初始化的。这就是为什么字段初始化器中的箭头函数对于实例字段绑定到实例，对于静态字段绑定到类。

```js
class C {
  instanceField = this;
  static staticField = this;
}

const c = new C();
console.log(c.instanceField === c); // true
console.log(C.staticField === C); // true
```

#### 派生类构造函数

与基类构造函数不同，派生构造函数没有初始的 `this` 绑定。调用 `super()` 在构造函数中创建一个 `this` 绑定，基本上和求值以下代码的效果类似，其中 `Base` 是基类：

```js
this = new Base();
```

**警告：** 在调用 `super()` 之前引用 `this` 将抛出错误。

派生类在调用 `super()` 之前不能有返回，除非构造函数返回一个对象（这样 `this` 值就会被覆盖）或者类根本没有构造函数。

```js
class Base {}
class Good extends Base {}
class AlsoGood extends Base {
  constructor() {
    return { a: 5 };
  }
}
class Bad extends Base {
  constructor() {}
}

new Good();
new AlsoGood();
new Bad(); // ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor
```

### 全局上下文

在全局执行上下文中（在任何函数或类之外；可能在全局范围内定义的块或箭头函数内部），`this` 值取决于脚本运行的执行上下文。像回调一样，`this` 值由运行时环境（调用者）确定。

在脚本的顶层，无论是否在严格模式下，`this` 会指向`globalThis`。这通常与全局对象相同——例如，如果源代码放在 HTML 的 `<script>` 元素内并作为脚本执行，`this === window`。

**备注：** `globalThis` 通常与全局对象的概念相同（即向 `globalThis` 添加属性会使它们成为全局变量）——这对于浏览器和 Node 是这样的——但主机可以为 `globalThis` 提供与全局对象无关的不同值。

```js
// 在网页浏览器中，window 对象也是全局对象：
console.log(this === window); // true

this.b = "MDN";
console.log(window.b); // "MDN"
console.log(b); // "MDN"
```

如果源代码作为模块加载（对于 HTML，这意味着在 `<script>` 标签中添加 `type="module"`），在顶层，`this` 总是 `undefined`。

如果源代码使用 `eval()` 执行，`this` 与直接调用 eval 的闭合上下文相同，或者与间接调用 eval 的 `globalThis`（就像它在单独的全局脚本中运行一样）相同。

```js
function test() {
  // 直接调用 eval
  console.log(eval("this") === this);
  // 间接调用 eval，非严格模式
  console.log(eval?.("this") === globalThis);
  // 间接调用 eval，严格模式
  console.log(eval?.("'use strict'; this") === globalThis);
}

test.call({ name: "obj" }); // 输出 3 个 "true"
```

请注意，某些源代码虽然看起来像全局作用域，但在执行时实际上被包装在一个函数中。例如，Node.js CommonJS 模块被包装在一个函数中，并且 `this` 值设置为 `module.exports`。事件处理器属性执行时，`this` 设置为它们附加到的元素。

对象字面量不创建 `this` 作用域——只有在对象内定义的函数（方法）才会这样做。在对象字面量中使用 `this` 会从周围的作用域继承值。

```js
const obj = {
  a: this,
};

console.log(obj.a === window); // true
```

示例
--

### 函数上下文中的 this

`this` 参数的值取决于函数如何被调用，而不是它如何被定义。

```js
// 对象可以作为第一个参数传递给 'call' 或 'apply'，
// 并且 'this' 将被绑定到它。
const obj = { a: "Custom" };

// 使用 var 声明的变量成为 'globalThis' 的属性。
var a = "Global";

function whatsThis() {
  return this.a; // 'this' 取决于函数如何被调用
}

whatsThis(); // 'Global'; 在非严格模式下，'this' 参数默认为 'globalThis'
obj.whatsThis = whatsThis;
obj.whatsThis(); // 'Custom'; 'this' 参数被绑定到 obj
```

使用 `call()` 和 `apply()`，你可以对 `this` 进行传值，就像它是一个显式参数。

```js
function add(c, d) {
  return this.a + this.b + c + d;
}

const o = { a: 1, b: 3 };

// 第一个参数被绑定到隐式的 'this' 参数；
// 剩余的参数被绑定到命名参数。
add.call(o, 5, 7); // 16

// 第一个参数被绑定到隐式的 'this' 参数；
// 第二个参数是一个数组，其成员被绑定到命名参数。
add.apply(o, [10, 20]); // 34
```

### this 和对象转换

在非严格模式下，如果一个函数被调用时其 `this` 值不是一个对象，那么 `this` 值会被替换为一个对象。`null` 和 `undefined` 会变成 `globalThis`。像 `7` 或 `'foo'` 这样的原始值会使用相关的构造函数转换为对象，所以原始数值 `7` 会被转换为一个 `Number` 包装类，字符串 `'foo'` 会被转换为一个 `String` 包装类。

```js
function bar() {
  console.log(Object.prototype.toString.call(this));
}

bar.call(7); // [object Number]
bar.call("foo"); // [object String]
bar.call(undefined); // [object Window]
```

### bind() 方法

调用 `f.bind(someObject)` 会创建一个新函数，这个新函数具有与 `f` 相同的函数体和作用域，但 `this` 的值永久绑定到 `bind` 的第一个参数，无论函数如何被调用。

```js
function f() {
  return this.a;
}

const g = f.bind({ a: "azerty" });
console.log(g()); // azerty

const h = g.bind({ a: "yoo" }); // bind 只能生效一次！
console.log(h()); // azerty

const o = { a: 37, f, g, h };
console.log(o.a, o.f(), o.g(), o.h()); // 37 37 azerty azerty
```

### 箭头函数中的 this

箭头函数在闭合执行上下文中创建了 `this` 值的闭包。在下面的例子中，我们创建了一个对象 `obj`，它有一个方法 `getThisGetter`，该方法返回一个函数，这个函数返回 `this` 的值。返回的函数是作为箭头函数的形式创建的，所以它的 `this` 永久地绑定到其执行上下文中的 `this`。`getThisGetter` 内部的 `this` 值可以在调用中设置，这反过来又设置了返回函数的返回值。我们假设 `getThisGetter` 是一个非严格函数，这意味着它包含在一个非严格模式的脚本中，并且没有进一步嵌套在类或严格模式的函数中。

```js
const obj = {
  getThisGetter() {
    const getter = () => this;
    return getter;
  },
};
```

我们可以作为 `obj` 的方法调用 `getThisGetter`，这将在其主体内部将 `this` 绑定到 `obj`。返回的函数被赋值给一个变量 `fn`。现在，当调用 `fn` 时，返回的 `this` 值仍然是通过调用 `getThisGetter` 设置的值，即 `obj`。如果返回的函数不是箭头函数，那么这样的调用会导致 `this` 值为 `globalThis`，因为 `getThisGetter` 是非严格模式的。

```js
const fn = obj.getThisGetter();
console.log(fn() === obj); // true
```

但是，如果你解绑 `obj` 的方法而不调用它，需要小心，因为 `getThisGetter` 仍然是一个方法，它有一个可变的 `this` 值。在下面的例子中，调用 `fn2()()` 将返回 `globalThis`，因为它遵循 `fn2()` 的 `this`，由于它没有附加到任何对象上进行调用，所以是 `globalThis`。

```js
const fn2 = obj.getThisGetter;
console.log(fn2()() === globalThis); // 在非严格模式下为 true
```

这种行为在定义回调时非常有用。通常，每个函数表达式都创建自己的 `this` 绑定，这会遮蔽上层作用域的 `this` 值。现在，如果你不关心 `this` 值，你可以将函数定义为箭头函数，并且只在你需要的地方创建 `this` 绑定（例如，在类方法中）。参见 `setTimeout()` 的示例。

### getter 或 setter 中的 this

在 getter 和 setter 中，`this` 是基于访问属性的对象，而不是定义属性的对象。用作 getter 或 setter 的函数会将其 `this` 绑定到正在设置或获取属性的对象。

```js
function sum() {
  return this.a + this.b + this.c;
}

const o = {
  a: 1,
  b: 2,
  c: 3,
  get average() {
    return (this.a + this.b + this.c) / 3;
  },
};

Object.defineProperty(o, "sum", {
  get: sum,
  enumerable: true,
  configurable: true,
});

console.log(o.average, o.sum); // 2 6
```

### DOM 事件处理器中的 this

当一个函数被用作事件处理器时，它的 `this` 参数绑定到放置监听器的 DOM 元素上（一些浏览器对于使用 `addEventListener()` 以外的方法动态添加的监听器并不遵循这个约定）。

```js
// 当作为监听器调用时，将相关元素变为蓝色
function bluify(e) {
  // 总是为 true
  console.log(this === e.currentTarget);
  // 当 currentTarget 和 target 是同一个对象时为 true
  console.log(this === e.target);
  this.style.backgroundColor = "#A5D9F3";
}

// 获取文档中的每一个元素
const elements = document.getElementsByTagName("\*");

// 添加 bluify 作为点击监听器，所以当元素被点击时，它会变蓝
for (const element of elements) {
  element.addEventListener("click", bluify, false);
}
```

### 内联事件处理器中的 this

当代码从内联事件处理器属性调用时，它的 `this` 绑定到放置监听器的 DOM 元素上：

```html
<button onclick="alert(this.tagName.toLowerCase());">Show this</button>
```

上面的 alert 会显示 `button`。注意只有外层代码中的 `this` 是这样设置的：

```html
<button onclick="alert((function(){return this})());">Show inner this</button>
```

在这种情况下，内部函数的 `this` 指向 `globalThis` 对象（即非严格模式下，调用的函数未设置 `this` 时指向的默认对象）。

### 类中的绑定方法

和其他普通函数一样，方法中的 `this` 值取决于它们如何被调用。有时，改写这个行为，让类中的 `this` 值总是指向这个类实例会很有用。为了做到这一点，可在构造函数中绑定类方法：

```js
class Car {
  constructor() {
    // 绑定 sayBye 而不是 sayHi 来展示差异
    this.sayBye = this.sayBye.bind(this);
  }
  sayHi() {
    console.log(`Hello from ${this.name}`);
  }
  sayBye() {
    console.log(`Bye from ${this.name}`);
  }
  get name() {
    return "Ferrari";
  }
}

class Bird {
  get name() {
    return "Tweety";
  }
}

const car = new Car();
const bird = new Bird();

// 方法中 'this' 的值取决于它们的调用者
car.sayHi(); // Hello from Ferrari
bird.sayHi = car.sayHi;
bird.sayHi(); // Hello from Tweety

// 对于绑定方法，'this' 不依赖于调用者
bird.sayBye = car.sayBye;
bird.sayBye(); // Bye from Ferrari
```

**备注：** 类总是在严格模式下。如果一个方法试图访问 `this` 上的属性，使用未定义的 `this` 值调用该方法将会抛出错误。

```js
const carSayHi = car.sayHi;
carSayHi(); // TypeError because the 'sayHi' method tries to access 'this.name', but 'this' is undefined in strict mode.
```

然而，请注意，自动绑定的方法遭受的问题与使用箭头函数作为类属性相同：类的每个实例都会有其方法的自己的副本，这会增加内存使用。只在绝对必要的地方使用它。你也可以模仿 `Intl.NumberFormat.prototype.format()` (en-US) 的实现：定义属性作为一个 getter，当访问时返回一个绑定函数并保存它，这样函数只创建一次，并且只会在必要时创建。

### with 语句中的 this

尽管 `with` 语句已被弃用，并且在严格模式下不可用，但它们仍然是正常 `this` 绑定规则的一个例外。如果在 `with` 语句中调用了一个函数，并且该函数是作用域对象的属性，那么 `this` 值会绑定到作用域对象，就好像存在 `obj1.` 前缀一样。

```js
const obj1 = {
  foo() {
    return this;
  },
};

with (obj1) {
  console.log(foo() === obj1); // true
}
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-this-keyword |

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

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

参见
--

* 严格模式
* `globalThis`


null - JavaScript

null
====

`null` 值特指未不存在任何对象值。它是 JavaScript 的原始值之一，且在布尔运算中被视为假值。

尝试一下
----

语法
--

```js
null
```

描述
--

值 `null` 被写作字面量：`null`。不像 `undefined`，`null` 不是全局对象的属性。相反，`null` 是表示缺少的标识，指示变量未指向任何对象。在 API 中，`null` 常在预期的值应是一个对象，但又没有关联的对象的地方使用。

```js
// foo 不存在。它从未被定义或初始化：
foo; //ReferenceError: foo is not defined
```

```js
// 已知 foo 现在已经存在，但它没有类型和值：
const foo = null;
foo; //null
```

示例
--

### `null` 与 `undefined` 的不同点

当检查值是否为 `null` 或 `undefined` 时，请注意相等（==）与全等（===）运算符的区别，前者会执行类型转换：

```js
typeof null; // "object"（因历史原因而不是 "null"）
typeof undefined; // "undefined"
null === undefined; // false
null == undefined; // true
null === null; // true
null == null; // true
!null; // true
Number.isNaN(1 + null); // false
Number.isNaN(1 + undefined); // true
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-null-value |

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

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

参见
--

* `undefined`
* `NaN`
* `void`


super - JavaScript

super
=====

**super** 关键字用于访问对象字面量或类的原型（[[Prototype]]）上的属性，或调用父类的构造函数。

`super.prop` 和 `super[expr]` 表达式在类和对象字面量任何方法定义中都是有效的。`super(...args)` 表达式在类的构造函数中有效。

语法
--

```js
super([arguments]) // 调用父类的构造函数
super.propertyOnParent
super[expression]
```

描述
--

`super` 关键字有两种使用方式：作为“函数调用”（`super(...args)`），或作为“属性查询”（`super.prop` 和 `super[expr]`）。

**备注：** `super` 是一个关键字，并且有一些特殊的语法结构。`super` 不是一个指向原型对象的变量。试图读取 `super` 本身会导致 `SyntaxError`。

```js
const child = {
  myParent() {
    console.log(super); // SyntaxError: 'super' keyword unexpected here
  },
};
```

在派生类的构造函数体中（使用 `extends`），`super` 关键字可以作为“函数调用”（`super(...args)`）出现，它必须在使用 `this` 关键字之前和构造函数返回之前被调用。它调用父类的构造函数并绑定父类的公共字段，之后派生类的构造函数可以进一步访问和修改 `this`。

“属性查询”形式可以用来访问一个对象字面或类的 [[Prototype]] 的方法和属性。在一个类的主体中，`super` 的引用可以是父类的构造函数本身，也可以是构造函数的 `prototype`，这取决于执行环境是实例创建还是类的初始化。更多细节请参见示例部分。

注意，`super` 的引用是由 `super` 声明的类或对象字面决定的，而不是方法被调用的对象。因此，取消绑定或重新绑定一个方法并不会改变其中 `super` 的引用（尽管它们会改变 `this` 的引用）。你可以把 `super` 看作是类或对象字面范围内的一个变量，这些方法在它上面创建了一个闭包。(但也要注意，它实际上并不是一个变量，正如上面所解释的那样)。

当通过 `super` 设置属性时，该属性将被设置在 `this` 上。

示例
--

### 在类中使用 `super`

以下代码片段来自于 classes sample（在线演示）。这里调用 `super()` 是为了避免重复在 `Rectangle` 与 `Square` 的构造函数之间共同的部分。

```js
class Polygon {
  constructor(height, width) {
    this.name = "Rectangle";
    this.height = height;
    this.width = width;
  }
  sayName() {
    console.log("Hi, I am a ", this.name + ".");
  }
  get area() {
    return this.height \* this.width;
  }
  set area(value) {
    this._area = value;
  }
}

class Square extends Polygon {
  constructor(length) {
    this.height; // ReferenceError，super 需要先被调用！

    // 这里，它调用父类的构造函数并传入 length
    // 作为 Polygon 的 height, width
    super(length, length);

    // 注意：在派生的类中，在你可以使用 'this' 之前，必须先调用 super()。
    // 现在可以使用 'this' 了，忽略 'this' 将导致引用错误（ReferenceError）
    this.name = "Square";
  }
}
```

### 调用父类上的静态方法

你也可以用 `super` 调用父类的静态方法。

```js
class Rectangle {
  static logNbSides() {
    return "I have 4 sides";
  }
}

class Square extends Rectangle {
  static logDescription() {
    return `${super.logNbSides()} which are all equal`;
  }
}
Square.logDescription(); // 'I have 4 sides which are all equal'
```

### 在类字段声明中访问 super

`super` 也可以在类字段初始化时被访问。`super` 的引用取决于当前字段是一个实例字段还是一个静态字段。

```js
class Base {
  static baseStaticField = 90;
  baseMethod() {
    return 10;
  }
}
class Extended extends Base {
  extendedField = super.baseMethod(); // 10
  static extendedStaticField = super.baseStaticField; // 90
}
```

注意，实例字段是在实例上设置的，而不是在构造函数的原型上，所以你不能用 `super` 来访问父类的实例字段。

```js
class Base {
  baseField = 10;
}
class Extended extends Base {
  extendedField = super.baseField; // undefined
}
```

在这里，`extendedField` 是 `undefined` 而不是 10，因为 `baseField` 被定义为 `Base` 实例的自有属性，而不是 `Base.prototype`。在这种情况下，`super` 只查找 `Base.prototype` 的属性，因为它是 `Extended.prototype` 的 [[Prototype]]。

### 删除 super 上的属性将抛出异常

你不能使用 delete 操作符加 `super.prop` 或者 `super[expr]` 去删除父类的属性，这样做会抛出 `ReferenceError`。

```js
class Base {
  foo() {}
}
class Derived extends Base {
  delete() {
    delete super.foo; // 这很糟糕
  }
}

new Derived().delete(); // ReferenceError: invalid delete involving 'super'.
```

### 在对象字面量中使用 super.prop

`super` 也可以在对象初始化器/对象字面量符号中使用。在这个例子中，两个对象定义了一个方法。在第二个对象中，`super` 调用第一个对象的方法。这是在 `Object.setPrototypeOf()` 的帮助下实现的，我们将 `obj2` 的原型设置为 `obj1`，这样 `super` 就能够在 `obj1` 上找到 `method1`。

```js
const obj1 = {
  method1() {
    console.log("method 1");
  },
};
const obj2 = {
  method2() {
    super.method1();
  },
};
Object.setPrototypeOf(obj2, obj1);
obj2.method2(); // logs "method 1"
```

### 读取 super.prop 的方法在绑定到其他对象时不会有不同的表现

访问 `super.x` 的行为类似于 `Reflect.get(Object.getPrototypeOf(objectLiteral), "x", this)`，这意味着该属性总是在对象字面/类声明的原型上寻找，取消绑定和重新绑定方法不会改变 `super` 的引用。

```js
class Base {
  baseGetX() {
    return 1;
  }
}
class Extended extends Base {
  getX() {
    return super.baseGetX();
  }
}
const e = new Extended();
console.log(e.getX()); // 1
const { getX } = e;
console.log(getX()); // 1
```

同样的情况也发生在对象字面量中。

```js
const parent1 = { prop: 1 };
const parent2 = { prop: 2 };
const child = {
  myParent() {
    console.log(super.prop);
  },
};
Object.setPrototypeOf(child, parent1);
child.myParent(); // 打印 "1"
const myParent = child.myParent;
myParent(); // 依然打印 "1"
const anotherChild = { \_\_proto\_\_: parent2, myParent };
anotherChild.myParent(); //依然打印 "1"
```

只有重设整个继承链才能改变 `super` 的引用。

```js
class Base {
  baseGetX() {
    return 1;
  }
  static staticBaseGetX() {
    return 3;
  }
}
class AnotherBase {
  baseGetX() {
    return 2;
  }
  static staticBaseGetX() {
    return 4;
  }
}
class Extended extends Base {
  getX() {
    return super.baseGetX();
  }
  static staticGetX() {
    return super.staticBaseGetX();
  }
}
const e = new Extended();
// 重置实例部分的继承
Object.setPrototypeOf(Extended.prototype, AnotherBase.prototype);
console.log(e.getX()); // 打印 "2" 而不是 "1", 因为原型链已经改变
console.log(Extended.staticGetX()); // 依然打印 "3", 因为我们还没有修改静态部分。
// 重置静态部分的继承
Object.setPrototypeOf(Extended, AnotherBase);
console.log(Extended.staticGetX()); //现在打印 "4"
```

### 设置 super.prop 将在此基础上设置属性

设置 `super` 的属性，比如 `super.x = 1`，就像 `Reflect.set(Object.getPrototypeOf(objectLiteral), "x", 1, this)` 的行为。这是一个将 `super` 简单理解为“原型对象的引用”的情况，因为它实际上是在 `this` 上设置属性。

```js
class A {}
class B extends A {
  setX() {
    super.x = 1;
  }
}
const b = new B();
b.setX();
console.log(b); // B { x: 1 }
console.log(Object.hasOwn(b, "x")); // true
```

`super.x = 1` 将在 `A.prototype` 上寻找 `x` 的属性描述符（并调用那里定义的 setter），但 `this` 的值将被设置为 `this`，在这种情况下就是 `b`。你可以阅读 `Reflect.set` 以了解更多关于 `target` 和 `receiver` 不同情况的细节。

这意味着，虽然 *get* `super.prop` 的方法通常不会受到 `this` 上下文的影响，但 *set* `super.prop` 的方法却容易受到影响。

```js
/\* Reusing same declarations as above \*/
const b2 = new B();
b2.setX.call(null); // TypeError: Cannot assign to read only property 'x' of object 'null'
```

然而，`super.x = 1` 仍然会查询原型对象的属性描述符，这意味着你不能重写不可写的属性，而且 setter 会被调用。

```js
class X {
  constructor() {
    // Create a non-writable property
    Object.defineProperty(this, "prop", {
      configurable: true,
      writable: false,
      value: 1,
    });
  }
}
class Y extends X {
  constructor() {
    super();
  }
  foo() {
    super.prop = 2; // Cannot overwrite the value.
  }
}
const y = new Y();
y.foo(); // TypeError: "prop" is read-only
console.log(y.prop); // 1
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-super-keyword |

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

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

参见
--

* 类


幂（**） - JavaScript

幂（\*\*）
=======

**幂**（**`**`**）运算符返回第一个操作数取第二个操作数的幂的结果。它等价于 `Math.pow()`，不同之处在于，它还接受 BigInt 作为操作数。

尝试一下
----

语法
--

```js
x \*\* y
```

描述
--

幂运算符是右结合的：`a ** b ** c` 等于 `a ** (b ** c)`。

在大多数语言里，比如 PHP、Python 等那些有幂运算符（`**`）的语言，幂运算符被定义有一个比一元运算符，比如一元的 `+` 和一元的 `-` 更高的运算顺序，但有一些例外。在 Bash 语言里，`**` 运算符被定义有一个比一元运算符更低的运算顺序。

在 JavaScript 中，不可能写出模棱两可的幂表达式。也就是说，你不能将一元运算符（`+/-/~/!/delete/void/typeof`）放在底数之前；这样做会导致语法错误 (en-US)。

例如，`-2 ** 2` 在 Bash 中为 4，但在其他语言（如 Python）中为 -4。这在 JavaScript 中是无效的，因为操作不明确。你必须在两边加上括号——例如，作为 `-(2 ** 2)`——以使意图明确。

请注意，某些编程语言使用插入符号 `^` 进行求幂，但 JavaScript 使用该符号表示逻辑异或运算符。

`NaN ** 0`（和等价的 `Math.pow(NaN, 0)`）是 `NaN` 不通过数学运算传播的唯一情况——尽管操作数是 `NaN`，但它返回 `1`。此外，`base` 为 1 且 `exponent` 为非有限（±Infinity 或 `NaN`）的行为与 IEEE 754 不同，IEEE 754 指定结果应为 1，而 JavaScript 返回 `NaN` 以保持与其原始行为的向后兼容性。

示例
--

### 基本求幂

```js
2 \*\* 3; // 8
3 \*\* 2; // 9
3 \*\* 2.5; // 15.588457268119896
10 \*\* -1; // 0.1
NaN \*\* 2; // NaN
NaN \*\* 0; // 1
1 \*\* Infinity; // NaN
```

### 结合性

```js
2 \*\* 3 \*\* 2; // 512
2 \*\* (3 \*\* 2); // 512
(2 \*\* 3) \*\* 2; // 64
```

### 与一元运算符一起使用

取幂表达式的值的相反数：

```js
-(2 \*\* 2); // -4
```

将幂表达式的底数转化为一个负数：

```js
(-2) \*\* 2; // 4
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-exp-operator |

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

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

参见
--

* 加法运算符
* 减法运算符
* 除法运算符
* 乘法运算符
* 取余运算符
* 自增运算符
* 自减运算符
* 一元减运算符
* 一元加运算符


函数表达式 - JavaScript

函数表达式
=====

**`function`** 关键字可以用来在一个表达式中定义一个函数。

你也可以使用 `Function` 构造函数和一个函数声明来定义函数。

语法
--
```

let function_expression = function [name]([param1[, param2[, ..., paramN]]]) {
   statements
};
```

从 ES2015开始，你也可以使用箭头函数 。

### 参数

`name`

函数名称。可被省略，此种情况下的函数是匿名函数（*anonymous*）。函数名称只是函数体中的一个本地变量。

`paramN`

被传递给函数的一个参数名称。一个函数至多拥有 255 个参数。

`statements`

构成函数体的语句。

描述
--

函数表达式（function expression）非常类似于函数声明（function statement）（详情查看函数声明），并且两者拥有几乎相同的语法。函数表达式与函数声明的最主要区别是函数名称（*function name*），在函数表达式中可省略它，从而创建匿名函数（*anonymous* functions）。一个函数表达式可以被用作一个 IIFE（Immediately Invoked Function Expression，即时调用的函数表达式），它一旦定义就运行。更多信息请查看函数。

### 函数表达式提升 (Function expression hoisting)

JavaScript 中的函数表达式没有提升，不像函数声明，你在定义函数表达式之前不能使用函数表达式：

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

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

### 具名函数表达式

如果你想在函数体内部引用当前函数，则需要创建一个具名函数表达式。**然后函数名称将会（且只会）作为函数体（作用域内）的本地变量**。这样也可以避免使用非标准的 arguments.callee 属性。

```js
var math = {
  factorial: function factorial(n) {
    if (n <= 1) return 1;
    return n \* factorial(n - 1);
  },
};
```

被函数表达式赋值的那个变量会有一个 name 属性，如果你把这个变量赋值给另一个变量的话，这个 name 属性的值也不会改变。如果函数是一个匿名函数，那 name 属性的值就是被赋值的变量的名称（隐藏值）。如果函数不是匿名的话，那 name 属性的值就是这个函数的名称（显性值）。这对于箭头函数也同样适用（箭头函数没有名字，所以你只能赋予 name 属性一个隐性名）。

```js
var foo = function () {};
foo.name; // "foo"

var foo2 = foo;
foo2.name; // "foo"

var bar = function baz() {};
bar.name; // "baz"

console.log(foo === foo2); //true
console.log(typeof baz); // undefined
console.log(bar === baz); // false (errors because baz == undefined)
```

示例
--

下面的例子定义了一个匿名函数并把它赋值给变量 x。这个函数返回它参数的平方：

```js
var x = function (y) {
  return y \* y;
};
```

更多情况下被当作回调函数使用：

```js
button.addEventListener("click", function (event) {
  console.log("button is clicked!");
});
```

规范
--

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

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

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

参见
--

* 箭头函数
* 函数
* `Function`
* function 语句


除法赋值（/=） - JavaScript

除法赋值（/=）
========

**除法赋值**（**`/=`**）运算符将变量除以右操作数的值，并将结果赋值给该变量。

尝试一下
----

语法
--

```js
x /= y // x = x / y
```

示例
--

### 使用除法赋值

```js
let bar = 5;

bar /= 2; // 2.5
bar /= 2; // 1.25
bar /= 0; // Infinity
bar /= "foo"; // NaN
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-assignment-operators |

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

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

参见
--

* JS 指南中的赋值运算符
* 除法运算符


自增（++） - JavaScript

自增（++）
======

**自增**（**`++`**）运算符对其操作数进行自增（加一），并根据运算符的位置返回自增之前或之后的值。

尝试一下
----

语法
--

```js
x++
++x
```

描述
--

如果使用后缀式，即将运算符放在操作数的后面（例如 `x++`），操作数会加一，然后返回加一之前的值。

如果使用前缀式，即将运算符放在操作数的前面（例如 `++x`），操作数会加一，然后返回加一之后的值。

自增运算符只能应用于引用的操作数（变量和对象属性，即有效的赋值目标）。`++x` 本身的计算结果是一个值，而不是一个引用，因此不能将多个自增运算符链接在一起。

```js
++(++x); // SyntaxError: Invalid left-hand side expression in prefix operation
```

示例
--

### 后缀式

```js
let x = 3;
const y = x++;

// x = 4
// y = 3
```

### 前缀式

```js
let x = 3;
const y = ++x;

// x = 4
// y = 4
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-postfix-increment-operator |

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

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

参见
--

* 加法运算符
* 减法运算符
* 除法运算符
* 乘法运算符
* 取余运算符
* 幂运算符
* 自减运算符
* 一元减运算符
* 一元加运算符


类表达式 - JavaScript

类表达式
====

**`class`** 关键字可用于在表达式中定义类。类似于函数表达式，类表达式可以是命名的，也可以是匿名的。如果命名，则类的名称只能在类体内部才能访问到。

尝试一下
----

语法
--

```js
const MyClass = class [className] [extends otherClassName] {
  // class body
}
```

描述
--

类表达式的语法类似于类声明。与 `class` 声明一样，`class` 表达式的主体在严格模式下执行。

类表达式和类声明之间存在一些差异，但是：

* 类表达式可以省略类名（“绑定标识符”），这在类声明中是不可能的。
* 类表达式允许你重新定义（重新声明）类**而不会抛出** `SyntaxError`。类声明不是这种情况。

`constructor` 方法是可选的。使用类表达式生成的类将始终响应 `typeof` 值为 `"function"`。

```js
"use strict";
let Foo = class {}; // constructor property is optional
Foo = class {}; // Re-declaration is allowed

typeof Foo; // returns "function"
typeof class {}; // returns "function"

Foo instanceof Object; // true
Foo instanceof Function; // true
class Foo {} // Throws SyntaxError (class declarations do not allow re-declaration)
```

示例
--

### 一个简单的类表达式

这只是一个简单的匿名类表达式，你可以使用变量 `Foo` 来引用它。

```js
const Foo = class {
  constructor() {}
  bar() {
    return "Hello World!";
  }
};

const instance = new Foo();
instance.bar(); // "Hello World!"
Foo.name; // "Foo"
```

### 命名类表达式

如果你想在类体内引用当前类，你可以创建一个*命名类表达式*。该名称仅在类表达式本身的范围内可见。

```js
const Foo = class NamedFoo {
  constructor() {}
  whoIsThere() {
    return NamedFoo.name;
  }
};
const bar = new Foo();
bar.whoIsThere(); // "NamedFoo"
NamedFoo.name; // ReferenceError: NamedFoo is not defined
Foo.name; // "NamedFoo"
```

规范
--

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

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

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

参见
--

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


加法赋值（+=） - JavaScript

加法赋值（+=）
========

加法赋值运算符（`+=`）将右操作数的值添加到变量，并将结果分配给该变量。两个操作数的类型决定了加法赋值运算符的行为，可能为加法或拼接。

尝试一下
----

语法
--

```js
x += y // x = x + y
```

示例
--

### 使用加法赋值

```js
let baz = true;

// Boolean + Number -> 加法
baz += 1; // 2

// Number + Boolean -> 加法
baz += false; // 2
```

```js
let foo = "foo";

// String + Boolean -> 拼接
foo += false; // "foofalse"

// String + String -> 拼接
foo += "bar"; // "foofalsebar"
```

```js
let bar = 5;

// Number + Number -> 加法
bar += 2; // 7

// Number + String -> 拼接
bar += "foo"; // "7foo"
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-assignment-operators |

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

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

参见
--

* JS 指南中的赋值运算符
* 加法运算符


自减（--） - JavaScript

自减（--）
======

**自减**（**`--`**）运算符对其操作数进行自减（减一），并根据运算符的位置返回自减之前或之后的值。

尝试一下
----

语法
--

```js
x--
--x
```

描述
--

如果使用后缀式，即将运算符放在操作数的后面（如，`x--`），操作数会减一，然后返回减一之前的值。

如果使用前缀式，即将运算符放在操作数的前面（如，`--x`），操作数会减一，然后返回减一之后的值。

自减运算符只能应用于引用的操作数（变量和对象属性，即有效的赋值目标）。`--x` 本身的计算结果是一个值，而不是一个引用，因此不能将多个自减运算符链接在一起。

```js
--(--x); // SyntaxError: Invalid left-hand side expression in prefix operation
```

示例
--

### 后缀式

```js
let x = 3;
const y = x--;

// x = 2
// y = 3
```

### 前缀式

```js
let x = 3;
const y = --x;

// x = 2
// y = 2
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-postfix-decrement-operator |

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

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

参见
--

* 加法运算符
* 减法运算符
* 除法运算符
* 乘法运算符
* 取余运算符
* 幂运算符
* 自增运算符
* 一元减运算符
* 一元加运算符


右移（>>） - JavaScript

右移（>>）
======

**右移运算符**（**`>>`**）将一个操作数的二进制表示形式向右移动指定位数，该操作数可以是数值或者 BigInt 类型。右边移出位被丢弃，左边移出的空位补符号位（最左边那位）。该操作也称为“符号位传播右移”（sign-propagating right shift）或“算术右移”（arithmetic right shift），因为返回值的符号位与第一个操作数的符号位相同。

尝试一下
----

语法
--

```js
x >> y
```

描述
--

`>>` 运算符针对这两种操作数的类型进行了重载：数值和 BigInt。对于数值，该运算符返回一个 32 位整数；对于 BigInt 类型，该运算符返回一个 BigInt。右移运算符首先将两个操作数强制转换为数值并测试它们的类型。如果两个操作数都转换成 BigInt，则执行 BigInt 右移；否则，它将两个操作数都转换为 32 位整数并执行数值右移。如果一个操作数变为 BigInt 而另一个变为数值，则会抛出 `TypeError`。

由于新的数字最左边位与之前数字的最左边位是相同值，故符号位（最左边的位）不会改变，因此被称为“符号位传播”（sign-propagating）。

运算符以二进制补码的形式对左操作数进行运算。考虑十进制（以 10 为底）数字 `9` 和 `-9` 的 `32` 位二进制表示：

```js
     9 (十进制): 00000000000000000000000000001001 (二进制)
    -9 (十进制): 11111111111111111111111111110111 (二进制)
```

十进制（以 10 为底）负数 `-9` 的二进制补码表示是将其相反数（即 `9`）的二进制表示 `00000000000000000000000000001001` 的所有位取反，再加 `1`。

这两个数的二进制表示的符号位由其最左边的位决定：对于十进制正数 `9`，二进制表示的最左边位为 `0`，对于十进制负数 `-9`，二进制表示的最左边位是 `1`。

给定十进制（以 10 为底）数 `9` 和 `-9` 的二进制表示：

`9 >> 2` 得到 `2`：

```js
     9 (十进制): 00000000000000000000000000001001 (二进制)
                  --------------------------------
9 >> 2 (十进制): 00000000000000000000000000000010 (二进制) = 2 (十进制)
```

请注意最右边的 `01` 是如何移出的，最左边的 `00` 是如何从左边移入的。

`-9 >> 2` 得到 `-3`：

```js
     -9 (十进制): 11111111111111111111111111110111 (二进制)
                   --------------------------------
-9 >> 2 (十进制): 11111111111111111111111111111101 (二进制) = -3 (十进制)
```

请注意最右边的 `11` 是如何移出的。但最左边的位情况稍有不同：由于 `-9` 是负数，它的符号位（即最左边的位）是 `1`，因此右移 2 位时会在最左边补上 `11`，这便保留了负号。

二进制表示 `11111111111111111111111111111101` 等于十进制（以 10 为底）负数 `-3`，因为所有负整数都以二进制补码的形式存储，而 `-3` 的二进制补码可以通过将十进制（以 10 为底）正数 `3` 的二进制表示（即 `00000000000000000000000000000011`）的所有位先取反，然后加 `1`。

如果左操作数是一个超过 32 位的数字，则会丢弃最高有效位。例如，以下超过 32 位的整数将被转换为 32 位整数：
```

丢弃前：11100110111110100000000000000110000000000001
丢弃后：            10100000000000000110000000000001
```

右操作数将被转换为无符号 32 位整数，然后取模 32，因此实际移位偏移量将始终是 0 到 31 之间的正整数，包括 0 和 31。例如，`100 >> 32` 与 `100 >> 0` 相同（结果都是 `100`），因为 32 模 32 为 0。

BigInt 在使用右移时不会产生截断现象。从概念上讲，正 BigInt 具有无限数量的前导位 `0`，而负 BigInt 具有无限数量的前导位 `1`。

将任何数字 `x` 右移 `0` 可以将 `x` 转换为 32 位整数。不要使用 `>> 0` 将数字截断为整数；使用 `Math.trunc()` 代替。

示例
--

### 使用右移操作

```js
9 >> 2; // 2
-9 >> 2; // -3

9n >> 2n; // 2n
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-signed-right-shift-operator |

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

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

参见
--

* JS 指南中的位运算
* 右移赋值运算符
* 无符号右移运算符


属性访问器 - JavaScript

属性访问器
=====

属性访问器提供了两种方式用于访问一个对象的属性，它们分别是点号和方括号。

尝试一下
----

语法
--
```

object.property
object['property']
```

描述
--

我们可以将对象看做是一个\*关联数组（\*或者：*映射*、*字典*、*哈希表*、*查询表*）。这个数组中的键就是这个对象中属性的名称。通常，当我们提及一个对象的属性时，会对属性与方法之间做个对比。然而，属性与方法之间的区别并不大。一个方法就是一个可以被调用的属性而已，例如一个指向函数 Function 实例的引用可以作为对象属性的值。

访问对象属性有两种方式：点号表示法和方括号表示法。

### 点号表示法

```js
get = object.property;
object.property = set;
```

以上代码中，`property`必须是一个有效的 JavaScript 标识符，例如，一串字母数字字符，也包括下划线及美元符号，但不能以数字作为开头。比如，`object.$1`是合法的，而 `object.1`是无效不合法的。

```js
document.createElement("pre");
```

在上述代码块中，`document`中存在一个名为"createElement"的方法并且被调用了。

如果对数字字面量使用方法，并且数字文字没有指数且没有小数点，请在方法调用之前的点之前留出空格，以防止点被解释为小数点。

```js
(77).toExponential();
// 或
(77).toExponential();
// 或
(77).toExponential();
// 或
(77).toExponential();
// 或
(77.0).toExponential();
// 因为 77. === 77.0，没有歧义（no ambiguity）
```

### 方括号表示法

```js
get = object[property_name];
object[property_name] = set;
```

`property_name` 是一个字符串。该字符串不一定是一个合法的标识符；它可以是任意值，例如，"1foo"，"!bar!"，甚至是 " "（一个空格）。

```js
document["createElement"]("pre");
```

这里的代码的功能跟上一个例子的作用是相同的。

括号之前允许有空格。

```js
document["createElement"]("pre");
```

### 属性名称

属性名称必须是字符串或符号 Symbol。这意味着非字符串对象不能用来作为一个对象属性的键。任何非字符串对象，包括 Number，都会通过 toString 方法，被转换成一个字符串。

```js
var object = {};
object["1"] = "value";
console.log(object[1]);
```

上述代码的输出为"value"，因为 1 被类型转换为'1'。

```js
var foo = { unique\_prop: 1 },
  bar = { unique\_prop: 2 },
  object = {};
object[foo] = "value";
console.log(object[bar]);
```

上述的代码的输出也是 "value"，由于对象 foo 和 bar 都会被转成相同的字符串。在SpiderMonkey JavaScript 引擎中，这个字符串是 "[object Object]"。

### 方法绑定

一个方法没有绑定到对象上，那就意味着这个方法是不起作用的。特别要注意的是，在一个方法中`this`对象并不是固定的，例如，`this`不需要指向包含当前方法的对象。`this`可通过函数调用被传递过去的值所替换。详见方法绑定。

### 注意`eval`

在那些可通过方括号表示法替换的场景下，JavaScript 新手在使用eval 经常会犯错。例如，下面的语法经常在很多代码中找到。

```js
x = eval("document.forms.form\_name.elements." + strFormControl + ".value");
```

`eval` 的性能较差，且有安全风险。在任何时候都应该避免使用。而且，此时 `strFormControl` 必须是一个合法的标识符，这在一些表单控件的 name、ID 值之中并不是必要的。所以，使用括号来代替会更好一些：

```js
x = document.forms["form\_name"].elements[strFormControl].value;
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-property-accessors |

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

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

参见
--

* `Object`
* `Object/defineProperty`


逗号运算符（,） - JavaScript

逗号运算符（,）
========

**逗号**（**`,`**）运算符对它的每个操作数从左到右求值，并返回最后一个操作数的值。这让你可以创建一个复合表达式，其中多个表达式被评估，复合表达式的最终值是其成员表达式中最右边的值。这通常用于为 `for` 循环提供多个参数。

尝试一下
----

语法
--

```js
expr1, expr2, expr3/\* , … \*/
```

参数
--

`expr1`、`expr2`、`expr3`……

一个或多个表达式，最后一个表达式的返回值会作为当前复合表达式的值。

描述
--

当你想要在期望一个表达式的位置包含多个表达式时，可以使用逗号运算符。这个运算符最常用的一种情况是：在 `for` 循环中提供多个参数。

逗号运算符与数组、对象和函数参数中的逗号含义完全不同。

示例
--

假设 `a` 是一个二维数组，每一维度包含 10 个元素，以下代码通过使用逗号运算符，可以同时递增 `i` 和递减 `j`。

下面的代码打印一个二维数组中斜线方向的元素：

```js
for (let i = 0, j = 9; i <= 9; i++, j--) {
  console.log(`a[${i}][${j}] = ${a[i][j]}`);
}
```

请注意，赋值中的逗号运算符可能看起来没有逗号运算符的正常效果，因为它们并不存在于表达式中。在以下示例中，`a` 被设置为 `b=3` 的值（它是 3），但是表达式 `c=4` 仍然会经过评估，且它的值（也就是 4）会返回至控制台。这是由于运算符的优先级和结合性。

```js
let a, b, c;

a = b = 3, c = 4; // 值 4 返回到控制台
console.log(a); // 3 (left-most)

let x, y, z;

x = (y = 5, z = 6); // 值 6 返回到控制台
console.log(x); // 6 (right-most)
```

### 处理后返回

另一个使用逗号运算符的例子是在返回值前处理一些操作。如前所述，逗号运算符只会返回最后一个元素，但其他表达式都会被求值。所以，可以这么做：

```js
function myFunc() {
  let x = 0;

  return (x += 1, x); // 与 return ++x; 等价
}
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-comma-operator |

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

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

参见
--

* `for` 循环


解构赋值 - JavaScript

解构赋值
====

**解构赋值**语法是一种 Javascript 表达式。可以将数组中的值或对象的属性取出，赋值给其他变量。

尝试一下
----

语法
--

```js
const [a, b] = array;
const [a, , b] = array;
const [a = aDefault, b] = array;
const [a, b, ...rest] = array;
const [a, , b, ...rest] = array;
const [a, b, ...{ pop, push }] = array;
const [a, b, ...[c, d]] = array;

const { a, b } = obj;
const { a: a1, b: b1 } = obj;
const { a: a1 = aDefault, b = bDefault } = obj;
const { a, b, ...rest } = obj;
const { a: a1, b: b1, ...rest } = obj;
const { [key]: a } = obj;

let a, b, a1, b1, c, d, rest, pop, push;
[a, b] = array;
[a, , b] = array;
[a = aDefault, b] = array;
[a, b, ...rest] = array;
[a, , b, ...rest] = array;
[a, b, ...{ pop, push }] = array;
[a, b, ...[c, d]] = array;

({ a, b } = obj); // brackets are required
({ a: a1, b: b1 } = obj);
({ a: a1 = aDefault, b = bDefault } = obj);
({ a, b, ...rest } = obj);
({ a: a1, b: b1, ...rest } = obj);
```

描述
--

对象和数组字面量表达式提供了一种简单的方法来创建*特别的*数据包。

```js
const x = [1, 2, 3, 4, 5];
```

解构赋值使用类似的语法，但在赋值的左侧定义了要从原变量中取出哪些值。

```js
const x = [1, 2, 3, 4, 5];
const [y, z] = x;
console.log(y); // 1
console.log(z); // 2
```

同样，你可以在赋值语句的左侧解构对象。

```js
const obj = { a: 1, b: 2 };
const { a, b } = obj;
// is equivalent to:
// const a = obj.a;
// const b = obj.b;
```

这种功能类似于 Perl 和 Python 等语言中存在的特性。

### 绑定与赋值

对于对象和数组的解构，有两种解构模式：*绑定模式*和*赋值模式*，它们的语法略有不同。

在绑定模式中，模式以声明关键字（`var`、`let` 或 `const`）开始。然后，每个单独的属性必须绑定到一个变量或进一步解构。

```js
const obj = { a: 1, b: { c: 2 } };
const {
  a,
  b: { c: d },
} = obj;
// Two variables are bound: `a` and `d`
```

所有变量共享相同的声明，因此，如果你希望某些变量可重新分配，而其他变量是只读的，则可能需要解构两次——一次使用 `let`，一次使用 `const`。

```js
const obj = { a: 1, b: { c: 2 } };
const { a } = obj; // a is constant
let {
  b: { c: d },
} = obj; // d is re-assignable
```

在赋值模式中，模式不以关键字开头。每个解构属性都被赋值给一个赋值目标——这个赋值目标可以事先用 `var` 或 `let` 声明，也可以是另一个对象的属性——一般来说，可以是任何可以出现在赋值表达式左侧的东西。

```js
const numbers = [];
const obj = { a: 1, b: 2 };
({ a: numbers[0], b: numbers[1] } = obj);
// The properties `a` and `b` are assigned to properties of `numbers`
```

**备注：** 当使用对象文字解构赋值而不带声明时，在赋值语句周围必须添加括号 `( ... )`。

`{ a, b } = { a: 1, b: 2 }` 不是有效的独立语法，因为左侧的 `{a, b}` 被视为块而不是对象字面量。但是，`({ a, b } = { a: 1, b: 2 })` 是有效的，`const { a, b } = { a: 1， b: 2 }` 也是有效的。

如果你的编码风格不包括尾随分号，则 `( ... )` 表达式前面需要有一个分号，否则它可能用于执行前一行的函数。

请注意，上述代码在等效的*绑定模式*中不是有效的语法：

```js
const numbers = [];
const obj = { a: 1, b: 2 };
const { a: numbers[0], b: numbers[1] } = obj;

// This is equivalent to:
// const numbers[0] = obj.a;
// const numbers[1] = obj.b;
// Which definitely is not valid.
```

### 默认值

每个解构属性都可以有一个*默认值*。当属性不存在或值为 `undefined` 时，将使用默认值。如果属性的值为 `null`，则不使用它。

```js
const [a = 1] = []; // a is 1
const { b = 2 } = { b: undefined }; // b is 2
const { c = 2 } = { c: null }; // c is null
```

默认值可以是任何表达式。仅在必要时对其进行评估。

```js
const { b = console.log("hey") } = { b: 2 };
// Does not log anything, because `b` is defined and there's no need
// to evaluate the default value.
```

### 剩余属性

你可以使用剩余属性（`...rest`）结束解构模式。此模式会将对象或数组的所有剩余属性存储到新的对象或数组中。

```js
const { a, ...others } = { a: 1, b: 2, c: 3 };
console.log(others); // { b: 2, c: 3 }

const [first, ...others2] = [1, 2, 3];
console.log(others2); // [2, 3]
```

剩余属性必须是模式中的最后一个，并且不能有尾随逗号。

```js
const [a, ...b,] = [1, 2, 3];

// SyntaxError: rest element may not have a trailing comma
// Always consider using rest operator as the last element
```

### 使用其他语法解构模式

在许多语法中，语言为你绑定变量，你也可以使用解构模式。其中包括：

* `for...in` 和 `for...of` 循环中的循环变量；
* 函数参数；
* `catch` 绑定变量。

有关特定于数组或对象解构的功能，请参阅下面的各个示例。

示例
--

### 解构数组

#### 基本变量赋值

```js
const foo = ["one", "two", "three"];

const [red, yellow, green] = foo;
console.log(red); // "one"
console.log(yellow); // "two"
console.log(green); // "three"
```

#### 解构比源更多的元素

在从赋值语句右侧指定的长度为 *N* 的数组解构的数组中，如果赋值语句左侧指定的变量数量大于 *N*，则只有前 *N* 个变量被赋值。其余变量的值将是未定义。

```js
const foo = ["one", "two"];

const [red, yellow, green, blue] = foo;
console.log(red); // "one"
console.log(yellow); // "two"
console.log(green); // undefined
console.log(blue); //undefined
```

#### 交换变量

可以在一个解构表达式中交换两个变量的值。

没有解构赋值的情况下，交换两个变量需要一个临时变量（或者用低级语言中的异或交换技巧）。

```js
let a = 1;
let b = 3;

[a, b] = [b, a];
console.log(a); // 3
console.log(b); // 1

const arr = [1, 2, 3];
[arr[2], arr[1]] = [arr[1], arr[2]];
console.log(arr); // [1, 3, 2]
```

#### 解析一个从函数返回的数组

从一个函数返回一个数组是十分常见的情况。解构使得处理返回值为数组时更加方便。

在下面例子中，要让 `f()` 返回值 `[1, 2]` 作为其输出，可以使用解构在一行内完成解析。

```js
function f() {
  return [1, 2];
}

const [a, b] = f();
console.log(a); // 1
console.log(b); // 2
```

#### 忽略某些返回值

你可以忽略你不感兴趣的返回值：

```js
function f() {
  return [1, 2, 3];
}

const [a, , b] = f();
console.log(a); // 1
console.log(b); // 3

const [c] = f();
console.log(c); // 1
```

你也可以忽略全部返回值：

```js
[, ,] = f();
```

#### 使用绑定模式作为剩余属性

数组解构赋值的剩余属性可以是另一个数组或对象绑定模式。这允许你同时提取数组的属性和索引。

```js
const [a, b, ...{ pop, push }] = [1, 2];
console.log(a, b); // 1 2
console.log(pop, push); // [Function pop] [Function push]
```

```js
const [a, b, ...[c, d]] = [1, 2, 3, 4];
console.log(a, b, c, d); // 1 2 3 4
```

这些绑定模式甚至可以嵌套，只要每个剩余属性都在列表的最后。

```js
const [a, b, ...[c, d, ...[e, f]]] = [1, 2, 3, 4, 5, 6];
console.log(a, b, c, d, e, f); // 1 2 3 4 5 6
```

另一方面，对象解构只能有一个标识符作为剩余属性。

```js
const { a, ...{ b } } = { a: 1, b: 2 };
// SyntaxError: `...` must be followed by an identifier in declaration contexts

let a, b;
({ a, ...{ b } } = { a: 1, b: 2 });
// SyntaxError: `...` must be followed by an assignable reference in assignment contexts
```

#### 从正则表达式匹配项中提取值

当正则表达式的 `exec()` 方法找到匹配项时，它将返回一个数组，该数组首先包含字符串的整个匹配部分，然后返回与正则表达式中每个括号组匹配的字符串部分。解构赋值允许你轻易地提取出需要的部分，如果不需要，则忽略完整匹配。

```js
function parseProtocol(url) {
  const parsedURL = /^(\w+):\/\/([^/]+)\/(.\*)$/.exec(url);
  if (!parsedURL) {
    return false;
  }
  console.log(parsedURL);
  // ["https://developer.mozilla.org/zh-CN/docs/Web/JavaScript",
  // "https", "developer.mozilla.org", "zh-CN/docs/Web/JavaScript"]

  const [, protocol, fullhost, fullpath] = parsedURL;
  return protocol;
}

console.log(
  parseProtocol("https://developer.mozilla.org/zh-CN/docs/Web/JavaScript"),
);
// "https"
```

#### 在任何可迭代对象上使用数组解构

数组解构调用右侧的迭代协议。因此，任何可迭代对象（不一定是数组）都可以解构。

```js
const [a, b] = new Map([
  [1, 2],
  [3, 4],
]);
console.log(a, b); // [1, 2] [3, 4]
```

不可迭代对象不能解构为数组。

```js
const obj = { 0: "a", 1: "b", length: 2 };
const [a, b] = obj;
// TypeError: obj is not iterable
```

只有在分配所有绑定之前，才会迭代可迭代对象。

```js
const obj = {
  \*[Symbol.iterator]() {
    for (const v of [0, 1, 2, 3]) {
      console.log(v);
      yield v;
    }
  },
};
const [a, b] = obj; // Only logs 0 and 1
```

其余的绑定会提前求值并创建一个新数组，而不是使用旧的迭代器。

```js
const obj = {
  \*[Symbol.iterator]() {
    for (const v of [0, 1, 2, 3]) {
      console.log(v);
      yield v;
    }
  },
};
const [a, b, ...rest] = obj; // Logs 0 1 2 3
console.log(rest); // [2, 3] (an array)
```

### 解构对象

#### 基本赋值

```js
const user = {
  id: 42,
  isVerified: true,
};

const { id, isVerified } = user;

console.log(id); // 42
console.log(isVerified); // true
```

#### 赋值给新的变量名

可以从对象中提取属性，并将其赋值给名称与对象属性不同的变量。

```js
const o = { p: 42, q: true };
const { p: foo, q: bar } = o;

console.log(foo); // 42
console.log(bar); // true
```

举个例子，`const { p: foo } = o` 从对象 `o` 中获取名为 `p` 的属性，并将其赋值给名为 `foo` 的局部变量。

#### 赋值到新的变量名并提供默认值

一个属性可以同时是两者：

* 从对象提取并分配给具有不同名称的变量。
* 指定一个默认值，以防获取的值为 `undefined`。

```js
const { a: aa = 10, b: bb = 5 } = { a: 3 };

console.log(aa); // 3
console.log(bb); // 5
```

#### 从作为函数参数传递的对象中提取属性

传递给函数参数的对象也可以提取到变量中，然后可以在函数体内访问这些变量。至于对象赋值，解构语法允许新变量具有与原始属性相同或不同的名称，并为原始对象未定义属性的情况分配默认值。

请考虑此对象，其中包含有关用户的信息。

```js
const user = {
  id: 42,
  displayName: "jdoe",
  fullName: {
    firstName: "Jane",
    lastName: "Doe",
  },
};
```

在这里，我们展示了如何将传递对象的属性提取到具有相同名称的变量。参数值 `{ id }` 表示传递给函数的对象的 `id` 属性应该被提取到一个同名变量中，然后可以在函数中使用。

```js
function userId({ id }) {
  return id;
}

console.log(userId(user)); // 42
```

你可以定义提取变量的名称。在这里，我们提取名为 `displayName` 的属性，并将其重命名为 `dname`，以便在函数体内使用。

```js
function userDisplayName({ displayName: dname }) {
  return dname;
}

console.log(userDisplayName(user)); // `jdoe`
```

嵌套对象也可以提取。下面的示例展示了属性 `fullname.firstName` 被提取到名为 `name` 的变量中。

```js
function whois({ displayName, fullName: { firstName: name } }) {
  return `${displayName} is ${name}`;
}

console.log(whois(user)); // "jdoe is Jane"
```

#### 设置函数参数的默认值

默认值可以使用 `=` 指定，如果指定的属性在传递的对象中不存在，则将其用作变量值。

下面我们展示了一个默认大小为 `big`的函数，默认坐标为 `x: 0, y: 0`，默认半径为 25。

```js
function drawChart({
  size = "big",
  coords = { x: 0, y: 0 },
  radius = 25,
} = {}) {
  console.log(size, coords, radius);
  // do some chart drawing
}

drawChart({
  coords: { x: 18, y: 30 },
  radius: 30,
});
```

在上面 `drawChart` 的函数签名中，解构的左侧具有空对象 `= {}` 的默认值。

你也可以在没有该默认值的情况下编写该函数。但是，如果你省略该默认值，该函数将在调用时寻找至少一个参数来提供，而在当前形式下，你可以在不提供任何参数的情况下调用 `drawChart()`。否则，你至少需要提供一个空对象字面量。

有关详细信息，请参阅默认参数值 > 有默认值的解构参数。

#### 解构嵌套对象和数组

```js
const metadata = {
  title: "Scratchpad",
  translations: [
    {
      locale: "de",
      localization\_tags: [],
      last\_edit: "2014-04-14T08:43:37",
      url: "/de/docs/Tools/Scratchpad",
      title: "JavaScript-Umgebung",
    },
  ],
  url: "/zh-CN/docs/Tools/Scratchpad",
};

let {
  title: englishTitle, // rename
  translations: [
    {
      title: localeTitle, // rename
    },
  ],
} = metadata;

console.log(englishTitle); // "Scratchpad"
console.log(localeTitle); // "JavaScript-Umgebung"
```

#### For of 迭代和解构

```js
const people = [
  {
    name: "Mike Smith",
    family: {
      mother: "Jane Smith",
      father: "Harry Smith",
      sister: "Samantha Smith",
    },
    age: 35,
  },
  {
    name: "Tom Jones",
    family: {
      mother: "Norah Jones",
      father: "Richard Jones",
      brother: "Howard Jones",
    },
    age: 25,
  },
];

for (const {
  name: n,
  family: { father: f },
} of people) {
  console.log(`Name: ${n}, Father: ${f}`);
}

// "Name: Mike Smith, Father: Harry Smith"
// "Name: Tom Jones, Father: Richard Jones"
```

#### 对象属性计算名和解构

计算属性名，如对象字面量，可以被解构。

```js
const key = "z";
const { [key]: foo } = { z: "bar" };

console.log(foo); // "bar"
```

#### 无效的 JavaScript 标识符作为属性名称

通过提供有效的替代标识符，解构可以与不是有效的 JavaScript 标识符的属性名称一起使用。

```js
const foo = { "fizz-buzz": true };
const { "fizz-buzz": fizzBuzz } = foo;

console.log(fizzBuzz); // true
```

### 解构基本类型

对象解构几乎等同于属性访问。这意味着，如果尝试解构基本类型的值，该值将被包装到相应的包装器对象中，并且在包装器对象上访问该属性。

```js
const { a, toFixed } = 1;
console.log(a, toFixed); // undefined ƒ toFixed() { [native code] }
```

与访问属性相同，解构 `null` 或 `undefined` 会抛出 `TypeError`。

```js
const { a } = undefined; // TypeError: Cannot destructure property 'a' of 'undefined' as it is undefined.
const { a } = null; // TypeError: Cannot destructure property 'b' of 'null' as it is null.
```

即使模式为空，也会发生这种情况。

```js
const {} = null; // TypeError: Cannot destructure 'null' as it is null.
```

#### 组合数组和对象解构

数组和对象解构可以组合使用。假设你想要下面 `props` 数组中的第三个元素，然后你想要对象中的 `name` 属性，你可以执行以下操作：

```js
const props = [
  { id: 1, name: "Fizz" },
  { id: 2, name: "Buzz" },
  { id: 3, name: "FizzBuzz" },
];

const [, , { name }] = props;

console.log(name); // "FizzBuzz"
```

#### 解构对象时查找原型链

当解构一个对象时，如果属性本身没有被访问，它将沿着原型链继续查找。

```js
const obj = {
  self: "123",
  \_\_proto\_\_: {
    prot: "456",
  },
};
const { self, prot } = obj;
// self "123"
// prot "456" (Access to the prototype chain)
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-destructuring-assignment |
| ECMAScript Language Specification # sec-destructuring-binding-patterns |

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

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

参见
--

* 赋值运算符
* "ES6 in Depth: Destructuring" on hacks.mozilla.org


一元减（-） - JavaScript

一元减（-）
======

**一元减**（**`-`**）运算符在其操作数之前，并对其取反。

尝试一下
----

语法
--

```js
-x
```

示例
--

### 数字取反

```js
const x = 3;
const y = -x;

// y = -3
// x = 3
```

### 非数字取反

一元减运算符可以将非数字转换为数字。

```js
const x = "4";
const y = -x;

// y = -4
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-unary-minus-operator |

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

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

参见
--

* 加法运算符
* 减法运算符
* 除法运算符
* 乘法运算符
* 取余运算符
* 求幂运算符
* 自增运算符
* 自减运算符
* 一元加运算符


异步函数（async function）表达式 - JavaScript

异步函数（async function）表达式
=======================

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

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

* Learn more
* See full compatibility
* Report feedback
**`async function`** 关键字可用于定义表达式中的异步函数。

你还可以使用异步函数声明。

语法
--

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

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

异步函数也可以使用箭头语法进行定义。

### 参数

`name` 可选

函数名称，可省略。如果省略则这个函数将成为*匿名*函数。该名称仅可在本函数中使用。

`paramN` 可选

传入函数的形参名称。

`statements` 可选

构成函数主体的语句。

描述
--

`async function` 表达式与异步函数语句非常相似，语法也基本相同。异步 `function` 表达式和异步 `function` 语句之间的主要区别在于*函数名称*，它可以在 `async function` 表达式中省略，从而创建一个*匿名*函数。`async function` 表达式可以用作 IIFE（立即执行函数表达式，Immediately Invoked Function Expression），它在定义后立即运行。参见函数章节以获取更多信息。

示例
--

### 简单示例

```js
function resolveAfter2Seconds(x) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(x);
    }, 2000);
  });
}

// 赋值给变量的异步函数表达式
const add = async function (x) {
  const a = await resolveAfter2Seconds(20);
  const b = await resolveAfter2Seconds(30);
  return x + a + b;
};

add(10).then((v) => {
  console.log(v); // 4 秒后打印 60
});

// 用作 IIFE 的异步函数表达式
(async function (x) {
  const p1 = resolveAfter2Seconds(20);
  const p2 = resolveAfter2Seconds(30);
  return x + (await p1) + (await p2);
})(10).then((v) => {
  console.log(v); // 2 秒后打印 60
});
```

规范
--

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

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

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

参见
--

* 异步函数
* `AsyncFunction` 对象
* `await`


除法（/） - JavaScript

除法（/）
=====

**除法**（**`/`**）运算符计算两个操作数的商，其中左操作数是被除数，右操作数是除数。

尝试一下
----

语法
--

```js
x / y
```

示例
--

### 基本除法

```js
1 / 2; // 0.5

Math.floor(3 / 2); // 1

1.0 / 2.0; // 0.5
```

### 除以零

```js
2.0 / 0; // Infinity

2.0 / 0.0; // Infinity, because 0.0 === 0

2.0 / -0.0; // -Infinity
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-multiplicative-operators |

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

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

参见
--

* 加法运算符
* 减法运算符
* 乘法运算符
* 取余运算符
* 幂运算符
* 自增运算符
* 自减运算符
* 一元减运算符
* 一元加运算符


减法（-） - JavaScript

减法（-）
=====

**减法**（**`-`**）运算符将两个操作数相减，并产生两者之差。

尝试一下
----

语法
--

```js
x - y
```

描述
--

减法运算符将两个操作数转换为数值，并根据两个操作数的类型执行数字减法或 BigInt 减法。如果类型不匹配，则抛出 `TypeError`。

示例
--

### 数值减法

```js
// Number - Number -> subtraction
5 - 3; // 2

// Number - Number -> subtraction
3 - 5; // -2
```

### 非数值减法

```js
// String - Number -> subtraction
"foo" - 3; // NaN; "foo" is converted to the number NaN

// Number - String -> subtraction
5 - "3"; // 2; "3" is converted to the number 3
```

### BigInt 减法

```js
// BigInt - BigInt -> subtraction
2n - 1n; // 1n
```

你不能在减法中混合使用 BigInt 和数字操作数。

```js
2n - 1; // TypeError: Cannot mix BigInt and other types, use explicit conversions
2 - 1n; // TypeError: Cannot mix BigInt and other types, use explicit conversions
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-subtraction-operator-minus |

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

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

参见
--

* 加法运算符
* 除法运算符
* 乘法运算符
* 取余运算符
* 幂运算符
* 自增运算符
* 自减运算符
* 一元减运算符
* 一元加运算符


in - JavaScript

in
==

如果指定的属性在指定的对象或其原型链中，则 **`in`** **运算符**返回 `true`。

尝试一下
----

语法
--
```

prop in object
```

### 参数

`prop`

一个字符串类型或者 symbol 类型的属性名或者数组索引（非 symbol 类型将会强制转为字符串）。

`objectName`

检查它（或其原型链）是否包含具有指定名称的属性的对象。

描述
--

下面的例子演示了一些 `in` 运算符的用法。

```js
// 数组
var trees = new Array("redwood", "bay", "cedar", "oak", "maple");
0 in trees; // 返回 true
3 in trees; // 返回 true
6 in trees; // 返回 false
"bay" in trees; // 返回 false (必须使用索引号，而不是数组元素的值)

"length" in trees; // 返回 true (length 是一个数组属性)

Symbol.iterator in trees; // 返回 true (数组可迭代，只在 ES2015+ 上有效)

// 内置对象
"PI" in Math; // 返回 true

// 自定义对象
var mycar = { make: "Honda", model: "Accord", year: 1998 };
"make" in mycar; // 返回 true
"model" in mycar; // 返回 true
```

`in`右操作数必须是一个对象值。例如，你可以指定使用`String`构造函数创建的字符串，但不能指定字符串文字。

```js
var color1 = new String("green");
"length" in color1; // 返回 true
var color2 = "coral";
"length" in color2; // 报错 (color2 不是对象)
```

### 对被删除或值为 undefined 的属性使用`in`

如果你使用 `delete` 运算符删除了一个属性，则 `in` 运算符对所删除属性返回 `false`。

```js
var mycar = { make: "Honda", model: "Accord", year: 1998 };
delete mycar.make;
"make" in mycar; // 返回 false

var trees = new Array("redwood", "bay", "cedar", "oak", "maple");
delete trees[3];
3 in trees; // 返回 false
```

如果你只是将一个属性的值赋值为`undefined`，而没有删除它，则 `in` 运算仍然会返回`true`。

```js
var mycar = { make: "Honda", model: "Accord", year: 1998 };
mycar.make = undefined;
"make" in mycar; // 返回 true
```

```js
var trees = new Array("redwood", "bay", "cedar", "oak", "maple");
trees[3] = undefined;
3 in trees; // 返回 true
```

### 继承属性

如果一个属性是从原型链上继承来的，`in` 运算符也会返回 `true`。

```js
"toString" in {}; // 返回 true
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-relational-operators |

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

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

参见
--

* `for...in`
* `delete`
* `Object.hasOwn()`
* `Reflect.has()`
* 属性的可枚举性和所有权


逻辑空赋值（??=） - JavaScript

逻辑空赋值（??=）
==========

逻辑空赋值运算符（`x ??= y`）仅在 `x` 是空值（`null` 或 `undefined`）时对其赋值。

尝试一下
----

语法
--

```js
expr1 ??= expr2
```

描述
--

### 短路评估

空值合并运算符从左至右求值，其使用以下规则测试是否可能进行语法短路求值：

`(结果非 null 或 undefined 的表达式) ?? expr` 被短路求值为左侧表达式，当左侧证明为既非 `null` 也非 `undefined`.

语法短路意味着 `expr` 部分**尚未被求值**，因此任何与其求值产生的相关副作用都不会生效（例如，如果 `expr` 是一个函数调用，则该调用将不会发生）。

逻辑空赋值的语法短路也意味着 `x ??= y` 等价于：

```js
x ?? (x = y);
```

而不等价于如下的表达式，因为其一定会发生赋值：

```js
x = x ?? y;
```

示例
--

### 使用逻辑空赋值

```js
function config(options) {
  options.duration ??= 100;
  options.speed ??= 25;
  return options;
}

config({ duration: 125 }); // { duration: 125, speed: 25 }
config({}); // { duration: 100, speed: 25 }
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-assignment-operators |

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

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

参见
--

* 空值合并运算符（`??`）
* 空值
* 真值
* 假值


逻辑或（

逻辑或（||）
=======

对于一组操作数的**逻辑或**（`||`，逻辑析取）运算符，当且仅当其一个或多个操作数为真，其运算结果为真。它通常与布尔（逻辑）值一起使用。当它是布尔值时，返回一个布尔值。然而，`||` 运算符实际上是返回一个指定的操作数的值，所以如果这个运算符被用于非布尔值，它将返回一个非布尔值。

尝试一下
----

语法
--

```js
expr1 || expr2
```

描述
--

如果 `expr1` 能转化为 `true`，返回 `expr1`；否则返回 `expr2`。

能够转化为 `true` 的值叫做真值，能够转化为 `false` 的值叫做假值。

能够转化为 false 的表达式的示例如下：

* `null`；
* `NaN`；
* `0`；
* 空字符串（`""` 或 `''` 或 ````）；
* `undefined`。

尽管 `||` 运算符可以与非布尔操作数一起使用，但它仍然被认为是一个布尔运算符，因为它的返回值总是可以被转换为布尔基本类型。要明确地将其返回值（或任何一般的表达式）转换为相应的布尔值，请使用双非运算符或 `Boolean` 构造函数。

### 短路求值

逻辑或从左到右进行求值，它使用如下规则进行“短路”测试：

`(some truthy expression) || expr` 被短路评估为真值。

短路意味着上面的 `expr` 部分**不被求值**，因此不会发生任何副作用（例如，如果 `expr` 是一个函数调用，调用就不会发生）。这是因为在对第一个操作数进行评估后，操作数的值已经确定。参见下述示例：

```js
function A() {
  console.log("调用了 A");
  return false;
}
function B() {
  console.log("调用了 B");
  return true;
}

console.log(B() || A());
// 由于调用了 B 函数，故会输出 "called B" 到控制台，
// 然后输出 true（运算符的运算结果）
```

### 运算符优先级

以下表达式看起来等价，但实际上不是，因为 `&&` 运算符比 `||` 运算符先执行（参见运算符优先级）。

```js
true || false && false; // 返回 true，因为 && 先执行
(true || false) && false; // 返回 false，因为分组运算符优先级更高
```

示例
--

### 使用或运算符

下列代码展示了 `||`（逻辑或）运算符的用法。

```js
true || true; // t || t returns true
false || true; // f || t returns true
true || false; // t || f returns true
false || 3 === 4; // f || f returns false
"Cat" || "Dog"; // t || t returns "Cat"
false || "Cat"; // f || t returns "Cat"
"Cat" || false; // t || f returns "Cat"
"" || false; // f || f returns false
false || ""; // f || f returns ""
false || varObject; // f || object returns varObject
```

**备注：** 如果你使用这个操作符为某些变量提供默认值，要注意任何*假*值都不会被使用。如果你只需要过滤掉 `null` 或 `undefined`，可以考虑使用空值合并运算符。

### 布尔值转化规则

#### 与运算符转化为或运算符

下述涉及到**布尔**运算符的运算：

```js
bCondition1 && bCondition2
```

总是与此表达式相同：

```js
!(!bCondition1 || !bCondition2)
```

#### 或运算符转化为与运算符

下述涉及到**布尔**运算符的运算：

```js
bCondition1 || bCondition2
```

总是与此表达式相同：

```js
!(!bCondition1 && !bCondition2)
```

### 去除嵌套的括号

由于逻辑表达式是从左到右进行求值的，只要遵循一定的规则，总是可以从一个复杂的表达式中删除括号。

下述涉及到**布尔**运算符的复杂运算：

```js
bCondition1 && (bCondition2 || bCondition3)
```

总是与此表达式相同：

```js
!(!bCondition1 || !bCondition2 && !bCondition3)
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # prod-LogicalORExpression |

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

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

参见
--

* 空值合并运算符（`??`）
* `Boolean`
* 真值
* 假值


按位或赋值（

按位或赋值（|=）
=========

**按位或赋值**（**`|=`**) 运算符使用两个操作数的二进制表示，对它们执行按位或运算并将结果分配给变量。

尝试一下
----

语法
--

```js
x |= y // x = x | y
```

示例
--

### 使用按位或赋值

```js
let a = 5;
a |= 2; // 7
// 5: 00000000000000000000000000000101
// 2: 00000000000000000000000000000010
// -----------------------------------
// 7: 00000000000000000000000000000111
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-assignment-operators |

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

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

参见
--

* JS 指南中的赋值运算符
* 按位或运算符
* 逻辑或赋值（`||=`）


大于（>） - JavaScript

大于（>）
=====

大于运算符（`>`）在左操作数大于右操作数时返回 `true`，否则返回 `false`。

尝试一下
----

语法
--

```js
x > y
```

描述
--

操作数比较使用与小于运算符相同的算法，只是两个操作数交换了。

示例
--

### 字符串与字符串比较

```js
console.log("a" > "b"); // false
console.log("a" > "a"); // false
console.log("a" > "3"); // true
```

### 字符串与数值比较

```js
console.log("5" > 3); // true
console.log("3" > 3); // false
console.log("3" > 5); // false

console.log("hello" > 5); // false
console.log(5 > "hello"); // false

console.log("5" > 3n); // true
console.log("3" > 5n); // false
```

### 数值与数值比较

```js
console.log(5 > 3); // true
console.log(3 > 3); // false
console.log(3 > 5); // false
```

### 数值与大整型比较

```js
console.log(5n > 3); // true
console.log(3 > 5n); // false
```

### 比较 Boolean、null、undefined 和 NaN

```js
console.log(true > false); // true
console.log(false > true); // false

console.log(true > 0); // true
console.log(true > 1); // false

console.log(null > 0); // false
console.log(1 > null); // true

console.log(undefined > 3); // false
console.log(3 > undefined); // false

console.log(3 > NaN); // false
console.log(NaN > 3); // false
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-relational-operators |

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

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

参见
--

* 大于等于运算符
* 小于运算符
* 小于等于运算符


减法赋值（-=） - JavaScript

减法赋值（-=）
========

**减法赋值**（**`-=`**）运算符从变量中减去右操作数的值，并将结果赋值给该变量。

尝试一下
----

语法
--

```js
x -= y // x = x - y
```

示例
--

### 使用减法赋值

```js
let bar = 5;

bar -= 2; // 3
bar -= "foo"; // NaN
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-assignment-operators |

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

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

参见
--

* JS 指南中的赋值运算符
* 减法运算符


按位与（&） - JavaScript

按位与（&）
======

**按位与**（**`&`**）运算符在两个操作数对应的二进位都为 `1` 时，该位的结果值才为 `1`。

尝试一下
----

语法
--

```js
a & b
```

描述
--

操作数被转换为 32 位整数，并由一系列位（0 和 1）表示。超过 32 位的数字将丢弃其最高有效位。例如，以下大于 32 位的整数将被转换为 32 位整数：
```

Before: 11100110111110100000000000000110000000000001
After:              10100000000000000110000000000001
```

第一个操作数中的每个位都与第二个操作数中的相应位配对：*第一位*到*第一位*，*第二位*到*第二位*，依此类推。

将运算符应用于每对位，然后按位构造结果。

与运算的真值表：

| a | b | a AND b |
| --- | --- | --- |
| 0 | 0 | 0 |
| 0 | 1 | 0 |
| 1 | 0 | 0 |
| 1 | 1 | 1 |
```

     9 (base 10) = 00000000000000000000000000001001 (base 2)
    14 (base 10) = 00000000000000000000000000001110 (base 2)
                   --------------------------------
14 & 9 (base 10) = 00000000000000000000000000001000 (base 2) = 8 (base 10)
```

任何数字 `x` 与 `0` 进行按位与运算都会得到 `0`。

示例
--

### 使用按位与

```js
// 5: 00000000000000000000000000000101
// 2: 00000000000000000000000000000010
5 & 2; // 0
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # prod-BitwiseANDExpression |

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

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

参见
--

* JS 指南中的位运算符
* 按位与赋值运算符


严格相等（===） - JavaScript

严格相等（===）
=========

严格相等运算符（`===`）会检查它的两个操作数是否相等，并且返回一个布尔值结果。与相等运算符不同，严格相等运算符总是认为不同类型的操作数是不同的。

尝试一下
----

语法
--

```js
x === y
```

描述
--

严格相等运算符（`===` 和 `!==`）提供了严格相等判定语义。

* 如果操作数的类型不同，则返回 `false`。
* 如果两个操作数都是对象，只有当它们指向同一个对象时才返回 `true`。
* 如果两个操作数都为 `null`，或者两个操作数都为 `undefined`，返回 `true`。
* 如果两个操作数有任意一个为 `NaN`，返回 `false`。
* 否则，比较两个操作数的值：
    + 数字类型必须拥有相同的数值。`+0` 和 `-0` 会被认为是相同的值。
    + 字符串类型必须拥有相同顺序的相同字符。
    + 布尔运算符必须同时为 `true` 或同时为 `false`。

严格相等运算符与相等（`==`）运算符最显著的区别是，如果操作数的类型不同，`==` 运算符会在比较之前尝试将它们转换为相同的类型。

示例
--

### 比较相同类型的操作数

```js
"hello" === "hello"; // true
"hello" === "hola"; // false

3 === 3; // true
3 === 4; // false

true === true; // true
true === false; // false

null === null; // true
```

### 比较不同类型的操作数

```js
"3" === 3; // false
true === 1; // false
null === undefined; // false
```

### 比较对象

```js
const object1 = {
  key: "value",
};

const object2 = {
  key: "value",
};

console.log(object1 === object2); // false
console.log(object1 === object1); // true
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-equality-operators |

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

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

参见
--

* 相等运算符
* 不相等运算符
* 严格不相等运算符


async function* 表达式 - JavaScript

async function\* 表达式
====================

**`async function*`** 关键字可用于在表达式中定义一个异步生成器函数。

你也可以使用 `async function*` 声明定义一个异步生成器函数。

尝试一下
----

语法
--

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

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

**备注：** 为了避免 `async function*` 声明所带来的歧义，表达式语句不能以关键字 `async function` 开头。`async function` 关键字仅在上下文中无法接受语句时，才会被视为表达式的开头。

### 参数

`name` 可选

函数名。在这种情况下，函数名是*匿名的*，可以被省略。该名称仅在函数主体的内部有效。

`paramN` 可选

传递给函数的参数名称。

`statements` 可选

构成函数主体的语句。

描述
--

`async function*` 表达式与 `async function*` 声明非常相似，语法几乎相同。*函数名*是 `async function*` 表达式和 `async function*` 声明之间最主要的区别，在 `async function*` 表达式中，可以创建*匿名*函数去忽略函数名。`async function*` 表达式可以用作立即调用函数表达式（IIFE），该表达式在被定义后立即运行，允许你去创建一个临时的异步的可迭代对象。有关更多信息，请参见函数这个章节。

示例
--

### 使用 async function\*

以下示例定义了一个没有名称的异步生成器函数并将它分配给变量 `x`。这个函数产生它参数的平方。

```js
const x = async function\* (y) {
  yield Promise.resolve(y \* y);
};
x(6)
  .next()
  .then((res) => console.log(res.value)); // 36
```

规范
--

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

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

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

参见
--

* `async function*` 语句
* `AsyncGeneratorFunction` 对象
* 迭代协议
* `GeneratorFunction` 对象
* `yield`
* `yield*`
* `Function` 对象
* 函数


空值合并运算符（??） - JavaScript

空值合并运算符（??）
===========

**空值合并运算符**（**`??`**）是一个逻辑运算符，当左侧的操作数为 `null` 或者 `undefined` 时，返回其右侧操作数，否则返回左侧操作数。

与逻辑或运算符（`||`）不同，逻辑或运算符会在左侧操作数为假值时返回右侧操作数。也就是说，如果使用 `||` 来为某些变量设置默认值，可能会遇到意料之外的行为。比如为假值（例如，`''` 或 `0`）时。见下面的例子。

尝试一下
----

语法
--

```js
leftExpr ?? rightExpr
```

示例
--

### 使用空值合并运算符

在这个例子中，我们使用空值合并运算符为常量提供默认值，保证常量不为 `null` 或者 `undefined`。

```js
const nullValue = null;
const emptyText = ""; // 空字符串，是一个假值，Boolean("") === false
const someNumber = 42;

const valA = nullValue ?? "valA 的默认值";
const valB = emptyText ?? "valB 的默认值";
const valC = someNumber ?? 0;

console.log(valA); // "valA 的默认值"
console.log(valB); // ""（空字符串虽然是假值，但不是 null 或者 undefined）
console.log(valC); // 42
```

### 为变量赋默认值

以前，如果想为一个变量赋默认值，通常的做法是使用逻辑或运算符（`||`）：

```js
let foo;

// foo is never assigned any value so it is still undefined
let someDummyText = foo || "Hello!";
```

然而，由于 `||` 是一个布尔逻辑运算符，左侧的操作数会被强制转换成布尔值用于求值。任何假值（`0`， `''`， `NaN`， `null`， `undefined`）都不会被返回。这导致如果你使用`0`，`''`或`NaN`作为有效值，就会出现不可预料的后果。

```js
let count = 0;
let text = "";

let qty = count || 42;
let message = text || "hi!";
console.log(qty); // 42，而不是 0
console.log(message); // "hi!"，而不是 ""
```

空值合并运算符可以避免这种陷阱，其只在第一个操作数为`null` 或 `undefined` 时（而不是其他假值）返回第二个操作数：

```js
let myText = ""; // An empty string (which is also a falsy value)

let notFalsyText = myText || "Hello world";
console.log(notFalsyText); // Hello world

let preservingFalsy = myText ?? "Hi neighborhood";
console.log(preservingFalsy); // '' (as myText is neither undefined nor null)
```

### 短路

与 OR 和 AND 逻辑运算符相似，当左表达式不为 `null` 或 `undefined` 时，不会对右表达式进行求值。

```js
function A() {
  console.log("函数 A 被调用了");
  return undefined;
}
function B() {
  console.log("函数 B 被调用了");
  return false;
}
function C() {
  console.log("函数 C 被调用了");
  return "foo";
}

console.log(A() ?? C());
// 依次打印 "函数 A 被调用了"、"函数 C 被调用了"、"foo"
// A() 返回了 undefined，所以运算符两边的表达式都被执行了

console.log(B() ?? C());
// 依次打印 "函数 B 被调用了"、"false"
// B() 返回了 false（既不是 null 也不是 undefined）
// 所以右侧表达式没有被执行
```

### 不能与 AND 或 OR 运算符共用

将 `??` 直接与 AND（`&&`）和 OR（`||`）运算符组合使用是不可取的。（译者注：应当是因为空值合并运算符和其他逻辑运算符之间的运算优先级/运算顺序是未定义的）这种情况下会抛出 `SyntaxError` 。

```js
null || undefined ?? "foo"; // 抛出 SyntaxError
true || undefined ?? "foo"; // 抛出 SyntaxError
```

但是，如果使用括号来显式表明运算优先级，是没有问题的：

```js
(null || undefined) ?? "foo"; // 返回 "foo"
```

### 与可选链式运算符（`?.`）的关系

空值合并运算符针对 `undefined` 与 `null` 这两个值，可选链式运算符（`?.`） 也是如此。在这访问属性可能为 `undefined` 与 `null` 的对象时，可选链式运算符非常有用。

```js
let foo = { someFooProp: "hi" };

console.log(foo.someFooProp?.toUpperCase()); // "HI"
console.log(foo.someBarProp?.toUpperCase()); // undefined
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # prod-CoalesceExpression |

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

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

参见
--

* 逻辑空赋值运算符（`??=`）
* 可选链运算符（`?.`）
* 逻辑或运算符（`||`）
* 函数中的默认参数值


右移赋值（>>=） - JavaScript

右移赋值（>>=）
=========

右移赋值运算符（`>>=`）将变量向右移动指定的位数，并将结果赋值给变量。

尝试一下
----

语法
--

```js
x >>= y // x = x >> y
```

示例
--

### 使用右移赋值

```js
let a = 5; // (00000000000000000000000000000101)
a >>= 2; // 1 (00000000000000000000000000000001)

let b = -5; // (-00000000000000000000000000000101)
b >>= 2; // -2 (-00000000000000000000000000000010)
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-assignment-operators |

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

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

参考
--

* JS 指南中的赋值运算符
* 右移运算符


import.meta - JavaScript

import.meta
===========

`import.meta`是一个给 JavaScript 模块暴露特定上下文的元数据属性的对象。它包含了这个模块的信息，比如说这个模块的 URL。

语法
--
```

import.meta
```

描述
--

`import.meta`对象由一个关键字`"import"`,一个点符号和一个`meta`属性名组成。通常情况下`"import."`是作为一个属性访问的上下文，但是在这里`"import"`不是一个真正的对象。

`import.meta`对象是由 ECMAScript 实现的，它带有一个`null`的原型对象。这个对象可以扩展，并且它的属性都是可写，可配置和可枚举的。

示例
--

这里有一个 `my-module.mjs 模块`

```html
<script type="module" src="my-module.mjs"></script>
```

你可以通过 `import.meta` 对象获取这个模块的元数据信息。

```js
console.log(import.meta); // { url: "file:///home/user/my-module.mjs" }
```

它返回一个带有`url`属性的对象，指明模块的基本 URL。也可以是外部脚本的 URL，还可以是内联脚本所属文档的 URL。

注意，url 也可能包含参数或者哈希（比如后缀`?`或`#`）

以下面的 HTML 为例：
```

<script type="module">
import './index.mjs?someURLInfo=5';
</script>
```

...下面的 JavaScript 会打印`someURLInfo`这个参数：
```

// index.mjs
new URL(import.meta.url).searchParams.get('someURLInfo'); // 5
```

在脚本中引入别的脚本同样生效：
```

// index.mjs
import './index2.mjs?someURLInfo=5';

// index2.mjs
new URL(import.meta.url).searchParams.get('someURLInfo'); // 5
```

虽然在上述例子中，Node.js 允许携带参数（或哈希），但以 Node 14.1.0 为例，使用`node --experimental-modules index.mjs?someURLInfo=5` 执行脚本，查询 URL 参数将会报错（该环境下`index.mjs?someURLInfo=5` 被视作一个文件而不是 URL）

像这种特定于文件的参数传递可能是对应用范围内`location.href`（ps: 通过在 HTML 路径添加参数或哈希）（而在 Node.js 中是`process.env`）的补充

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # prod-ImportMeta |
| HTML Standard # hostgetimportmetaproperties |

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

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

参见
--

* import
* `export`


左移赋值（<<=） - JavaScript

左移赋值（<<=）
=========

左移赋值运算符（`<<=`）将变量向左移动指定的位数，并将结果赋值给变量。

尝试一下
----

语法
--

```js
x <<= y // x = x << y
```

示例
--

### 使用左移赋值

```js
let a = 5;
// 00000000000000000000000000000101

a <<= 2; // 20
// 00000000000000000000000000010100
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-assignment-operators |

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

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

参考
--

* JS 指南中的赋值运算符
* 左移运算符


幂赋值（**=） - JavaScript

幂赋值（\*\*=）
==========

**幂赋值**（**`**=`**）对两个操作数执行幂运算，并将结果赋给左操作数。

尝试一下
----

语法
--

```js
x \*\*= y // x = x \*\* y
```

示例
--

### 使用幂赋值

```js
let bar = 5;

bar \*\*= 2; // 25
bar \*\*= "foo"; // NaN
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-assignment-operators |

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

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

参见
--

* JS 指南中的赋值运算符
* 幂运算符


new - JavaScript

new
===

**`new`** 运算符允许开发人员创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例。

尝试一下
----

语法
--

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

### 参数

`constructor`

一个指定对象实例的类型的类或函数。

`arg1`、`arg2`、……、`argN`

一个用于被 `constructor` 调用的值列表。`new Foo` 与 `new Foo()` 等价，换句话说：如果没有指定参数列表，则在不带参数的情况下调用 `Foo`。

描述
--

当使用 **`new`** 关键字调用函数时，该函数将被用作构造函数。`new` 将执行以下操作：

1. 创建一个空的简单 JavaScript 对象。为方便起见，我们称之为 `newInstance`。
2. 如果构造函数的 `prototype` 属性是一个对象，则将 `newInstance` 的 [[Prototype]] 指向构造函数的 `prototype` 属性，否则 `newInstance` 将保持为一个普通对象，其 [[Prototype]] 为 `Object.prototype`。

**备注：** 因此，通过构造函数创建的所有实例都可以访问添加到构造函数 `prototype` 属性中的属性/对象。
3. 使用给定参数执行构造函数，并将 `newInstance` 绑定为 `this` 的上下文（换句话说，在构造函数中的所有 `this` 引用都指向 `newInstance`）。
4. 如果构造函数返回非原始值，则该返回值成为整个 `new` 表达式的结果。否则，如果构造函数未返回任何值或返回了一个原始值，则返回 `newInstance`。（通常构造函数不返回值，但可以选择返回值，以覆盖正常的对象创建过程。）

类只能用 `new` 运算符实例化——尝试不使用 `new` 调用一个类将抛出 `TypeError`。

创建一个用户自定义的对象需要两步：

1. 通过编写指定对象名称和属性的函数来定义对象类型。例如，创建 `Foo` 对象的构造函数看起来可能像这样：

```js
function Foo(bar1, bar2) {
  this.bar1 = bar1;
  this.bar2 = bar2;
}
```

2. 通过 `new` 来创建对象实例。

```js
const myFoo = new Foo("Bar 1", 2021);
```

**备注：** 对象的属性可以是另一个对象。请参阅下面的示例。

你始终可以对已定义的对象添加新的属性。例如，`car1.color = "black"` 语句给 `car1` 添加了一个新的属性 `color`，并将其赋值为 `"black"`。

但是，这不会影响任何其他对象。要将新属性添加到相同类型的所有对象，你必须将该属性添加到构造函数的 `prototype` 属性中。其定义了由该函数创建的所有对象所共享的属性，而不仅仅是对象类型的其中一个实例。以下代码将一个值为 `"原色"` 的 `color` 属性添加到 `Car` 类型的所有对象，然后仅在实例对象 `car1` 中用字符串 `"黑色"` 覆盖该值。请参见原型以了解更多信息。

```js
function Car() {}
car1 = new Car();
car2 = new Car();

console.log(car1.color); // undefined

Car.prototype.color = "原色";
console.log(car1.color); // '原色'

car1.color = "黑色";
console.log(car1.color); // '黑色'

console.log(Object.getPrototypeOf(car1).color); // '原色'
console.log(Object.getPrototypeOf(car2).color); // '原色'
console.log(car1.color); // '黑色'
console.log(car2.color); // '原色'
```

**备注：** 虽然构造函数可以像任何常规函数一样被调用（即不使用 `new` 运算符），但在这种情况下并不会创建一个新的对象，`this` 的值也是不一样的。

函数可以通过检查 `new.target` 来知道它是否是通过 `new` 被调用的。当函数在没有使用 `new` 的情况下被调用时，`new.target` 的值为 `undefined`。例如，你可以有一个在被调用时和被构造时具有不同表现的函数：

```js
function Car(color) {
  if (!new.target) {
    // 以函数的形式被调用。
    return `${color}车`;
  }
  // 通过 new 被调用。
  this.color = color;
}

const a = Car("红"); // a 是“红车”
const b = new Car("红"); // b 是 `Car { color: "红" }`
```

在 ES6（引入了类）之前，大多数 JavaScript 内置对象既可调用也可构造，尽管其中许多对象表现出不同的行为。举几个例子：

* `Array()`、`Error()` 以及 `Function()` 在被调用时和被构造时表现一致。
* `Boolean()`、`Number()` 以及 `String()` 在被调用时将它们的参数强制转换为相应的原始类型，而在被构造时返回包装对象。
* `Date()` 在被调用时返回表示当前日期的字符串，相当于 `new Date().toString()`。

在 ES6 之后，语言对哪些是构造函数、哪些是函数有更严格的要求。例如：

* `Symbol()` 和 `BigInt()` 只能在不使用 `new` 的情况下被调用。尝试构造它们将抛出 `TypeError`。
* `Proxy` 和 `Map` 只能通过 `new` 构造。尝试调用它们将抛出 `TypeError`。
示例
--

### 对象类型和对象实例

假设你要创建一个汽车的对象类型。你希望这个类型叫做 `Car`，这个类型具备 make、model、year 属性。要做到这些，你需要编写以下函数：

```js
function Car(make, model, year) {
  this.make = make;
  this.model = model;
  this.year = year;
}
```

现在，你可以如下所示创建一个 `myCar` 的对象：

```js
const myCar = new Car("鹰牌", "Talon TSi", 1993);
```

该语句创建了 `myCar` 并将其属性赋为指定的值。于是 `myCar.make` 的值“鹰牌”，`myCar.year` 的值为整数 1993，以此类推。

你可以通过调用 `new` 来创建任意个 `car` 对象。例如：

```js
const kensCar = new Car("尼桑", "300ZX", 1992);
```

### 对象属性为其他对象

假设你定义了一个叫做 `Person` 的对象：

```js
function Person(name, age, sex) {
  this.name = name;
  this.age = age;
  this.sex = sex;
}
```

然后实例化了两个新的 `Person` 对象如下：

```js
const rand = new Person("兰德·麦克纳利", 33, "男性");
const ken = new Person("肯·琼斯", 39, "男性");
```

然后你可以重写 `Car` 的定义，添加一个值为 `Person` 对象的 `owner` 属性，如下：

```js
function Car(make, model, year, owner) {
  this.make = make;
  this.model = model;
  this.year = year;
  this.owner = owner;
}
```

要实例化新的对象，你可以用如下代码：

```js
const car1 = new Car("鹰牌", "Talon TSi", 1993, rand);
const car2 = new Car("尼桑", "300ZX", 1992, ken);
```

创建对象时，并没有传字符串或数字，而是将对象 `rand` 和 `ken` 作为参数传递，来代表所有者。要查找 `car2` 的所有者的名称，你可以访问以下属性：

```js
car2.owner.name;
```

### 使用 `new` 和类

```js
class Person {
  constructor(name) {
    this.name = name;
  }
  greet() {
    console.log(`你好，我的名字是${this.name}`);
  }
}

const p = new Person("卡罗琳");
p.greet(); // 你好，我的名字是卡罗琳
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-new-operator |

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

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

参见
--

* `Function`
* `Reflect.construct()`
* `Object`