recursal/MDN huggingface dataset:
加法（+）
=====

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

尝试一下
----

语法
--


```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.

参见
--

* 减法运算符
* 除法运算符
* 乘法运算符
* 取余运算符
* 求幂运算符
* 自增运算符
* 自减运算符
* 一元减运算符
* 一元加运算符
无符号右移赋值（>>>=）
=============

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

尝试一下
----

语法
--


```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 指南中的赋值运算符
* 无符号右移位运算符
小于（<）
=====

**小于**（`<`）运算符在左操作数比右操作数小时返回 `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.

参见
--

* 大于运算符
* 大于等于运算符
* 小于等于运算符
取余赋值（%=）
========

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

尝试一下
----

语法
--


```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 指南中的赋值运算符
* 取余运算符
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*`
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`
幂（\*\*）
=======

**幂**（**`**`**）运算符返回第一个操作数取第二个操作数的幂的结果。它等价于 `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.

参见
--

* 加法运算符
* 减法运算符
* 除法运算符
* 乘法运算符
* 取余运算符
* 自增运算符
* 自减运算符
* 一元减运算符
* 一元加运算符
函数表达式
=====

**`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 语句
除法赋值（/=）
========

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

尝试一下
----

语法
--


```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 指南中的赋值运算符
* 除法运算符
自增（++）
======

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

尝试一下
----

语法
--


```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.

参见
--

* 加法运算符
* 减法运算符
* 除法运算符
* 乘法运算符
* 取余运算符
* 幂运算符
* 自减运算符
* 一元减运算符
* 一元加运算符
类表达式
====

**`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.

参见
--

* 函数表达式
* 类声明
* 类
加法赋值（+=）
========

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

尝试一下
----

语法
--


```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 指南中的赋值运算符
* 加法运算符
自减（--）
======

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

尝试一下
----

语法
--


```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.

参见
--

* 加法运算符
* 减法运算符
* 除法运算符
* 乘法运算符
* 取余运算符
* 幂运算符
* 自增运算符
* 一元减运算符
* 一元加运算符
右移（>>）
======

**右移运算符**（**`>>`**）将一个操作数的二进制表示形式向右移动指定位数，该操作数可以是数值或者 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 指南中的位运算
* 右移赋值运算符
* 无符号右移运算符
属性访问器
=====

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

尝试一下
----

语法
--
```

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`
逗号运算符（,）
========

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

**一元减**（**`-`**）运算符在其操作数之前，并对其取反。

尝试一下
----

语法
--


```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）表达式
=======================

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`
除法（/）
=====

**除法**（**`/`**）运算符计算两个操作数的商，其中左操作数是被除数，右操作数是除数。

尝试一下
----

语法
--


```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.

参见
--

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