Object.values() - JavaScript

Object.values()
===============

**`Object.values()`** 静态方法返回一个给定对象的自有可枚举字符串键属性值组成的数组。

尝试一下
----

语法
--

```js
Object.values(obj)
```

### 参数

`obj`

一个对象。

### 返回值

一个包含了给定对象的自有可枚举字符串键属性值的数组。

描述
--

`Object.values()` 返回一个数组，其元素是直接在 `object` 上找到的可枚举字符串键属性值。这与使用 `for...in` 循环迭代相同，只是 `for...in` 循环还枚举原型链中的属性。`Object.values()` 返回的数组顺序和 `for...in` 循环提供的数组顺序相同。

如果需要属性键，请使用 `Object.keys()`。如果属性的键和值都需要，请使用 `Object.entries()`。

示例
--

### 使用 Object.values()

```js
const obj = { foo: "bar", baz: 42 };
console.log(Object.values(obj)); // ['bar', 42]

// 类数组对象
const arrayLikeObj1 = { 0: "a", 1: "b", 2: "c" };
console.log(Object.values(arrayLikeObj1)); // ['a', 'b', 'c']

// 具有随机键排序的类数组对象
// 使用数字键时，将按键的数字顺序返回值
const arrayLikeObj2 = { 100: "a", 2: "b", 7: "c" };
console.log(Object.values(arrayLikeObj2)); // ['b', 'c', 'a']

// getFoo 是一个不可枚举的属性
const myObj = Object.create(
  {},
  {
    getFoo: {
      value() {
        return this.foo;
      },
    },
  },
);
myObj.foo = "bar";
console.log(Object.values(myObj)); // ['bar']
```

### 在基本类型中使用 Object.values()

非对象参数会强制转换为对象。`undefined` 和 `null` 不能被强制转换为对象，会立即抛出 `TypeError`。只有字符串可以有自己的可枚举属性，而其他所有基本类型都返回一个空数组。

```js
// 字符串具有索引作为可枚举的自有属性
console.log(Object.values("foo")); // ['f', 'o', 'o']

// 其他基本类型（除了 undefined 和 null）没有自有属性
console.log(Object.values(100)); // []
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-object.values |

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

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

参见
--

* `core-js` 中 `Object.values` 的 Polyfill
* 属性的可枚举性和所有权
* `Object.keys()`
* `Object.entries()`
* `Object.prototype.propertyIsEnumerable()`
* `Object.create()`
* `Object.getOwnPropertyNames()`
* `Map.prototype.values()`


Object.getOwnPropertySymbols() - JavaScript

Object.getOwnPropertySymbols()
==============================

**`Object.getOwnPropertySymbols()`** 静态方法返回一个包含给定对象所有自有 Symbol 属性的数组。

尝试一下
----

语法
--

```js
Object.getOwnPropertySymbols(obj)
```

### 参数

`obj`

要返回 Symbol 属性的对象。

### 返回值

在给定对象找到的所有自有 Symbol 属性的数组。

描述
--

与 `Object.getOwnPropertyNames()` 类似，你可以将给定对象的所有符号属性作为 Symbol 数组获取。请注意，`Object.getOwnPropertyNames()` 本身不包含对象的 Symbol 属性，只包含字符串属性。

因为所有的对象在初始化的时候不会包含任何自有的 Symbol 属性，除非你在对象上分配了 Symbol 属性，否则 `Object.getOwnPropertySymbols()` 只会返回一个空的数组。

示例
--

### 使用 Object.getOwnPropertySymbols()

```js
const obj = {};
const a = Symbol("a");
const b = Symbol.for("b");

obj[a] = "localSymbol";
obj[b] = "globalSymbol";

const objectSymbols = Object.getOwnPropertySymbols(obj);

console.log(objectSymbols.length); // 2
console.log(objectSymbols); // [Symbol(a), Symbol(b)]
console.log(objectSymbols[0]); // Symbol(a)
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-object.getownpropertysymbols |

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

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

参见
--

* `core-js` 中 `Object.getOwnPropertySymbols` 的 polyfill
* `Object.getOwnPropertyNames()`
* `Symbol`


Object.prototype.propertyIsEnumerable() - JavaScript

Object.prototype.propertyIsEnumerable()
=======================================

**`propertyIsEnumerable()`** 方法返回一个布尔值，表示指定的属性是否是对象的可枚举自有属性。

尝试一下
----

语法
--

```js
propertyIsEnumerable(prop)
```

### 参数

`prop`

需要测试的属性名，可以是字符串或 `Symbol`。

### 返回值

一个布尔值，指示指定的属性是否可枚举并且是对象自有的属性。

描述
--

所有继承自 `Object.prototype`（即除了 `null` 原型对象之外的所有对象）都继承了 `propertyIsEnumerable()` 方法。该方法判断指定的属性（字符串或 symbol 值）是否是对象的可枚举自有属性。如果对象没有指定的属性，则该方法返回 `false`。

此方法等价于 `Object.getOwnPropertyDescriptor(obj, prop)?.enumerable ?? false`。

示例
--

### 使用 propertyIsEnumerable()

以下示例演示了在对象和数组上使用 `propertyIsEnumerable()`。

```js
const o = {};
const a = [];
o.prop = "是可枚举的";
a[0] = "是可枚举的";

o.propertyIsEnumerable("prop"); // true
a.propertyIsEnumerable(0); // true
```

### 用户自定义对象和内置对象

大多数内置属性默认情况下是不可枚举的，而用户创建的对象属性通常是可枚举的，除非明确指定为不可枚举。

```js
const a = ["是可枚举的"];

a.propertyIsEnumerable(0); // true
a.propertyIsEnumerable("length"); // false

Math.propertyIsEnumerable("random"); // false
globalThis.propertyIsEnumerable("Math"); // false
```

### 自有属性和继承属性

只有可枚举自有属性会令 `propertyIsEnumerable()` 返回 `true`，尽管 `for...in` 循环会访问所有可枚举属性，包括继承的属性。

```js
const o1 = {
  enumerableInherited: "是可枚举的",
};
Object.defineProperty(o1, "nonEnumerableInherited", {
  value: "是不可枚举的",
  enumerable: false,
});
const o2 = {
  // o1 是 o2 的原型
  \_\_proto\_\_: o1,
  enumerableOwn: "是可枚举的",
};
Object.defineProperty(o2, "nonEnumerableOwn", {
  value: "是不可枚举的",
  enumerable: false,
});

o2.propertyIsEnumerable("enumerableInherited"); // false
o2.propertyIsEnumerable("nonEnumerableInherited"); // false
o2.propertyIsEnumerable("enumerableOwn"); // true
o2.propertyIsEnumerable("nonEnumerableOwn"); // false
```

### 测试 Symbol 属性

`propertyIsEnumerable()` 也支持 `Symbol` 属性。请注意，大多数枚举方法只访问字符串属性；当使用 `Object.assign()` 或展开语法时，symbol 属性的可枚举性才有用。更多信息，请参见属性的枚举性和所有权。

```js
const sym = Symbol("可枚举的");
const sym2 = Symbol("不可枚举的");
const o = {
  [sym]: "是可枚举的",
};
Object.defineProperty(o, sym2, {
  value: "是不可枚举的",
  enumerable: false,
});

o.propertyIsEnumerable(sym); // true
o.propertyIsEnumerable(sym2); // false
```

### 在 null 原型对象上使用

由于 `null` 原型对象不从 `Object.prototype` 继承，它们不会继承 `propertyIsEnumerable()` 方法。你必须使用该对象作为 `this` 来调用 `Object.prototype.propertyIsEnumerable`。

```js
const o = {
  \_\_proto\_\_: null,
  enumerableOwn: "是可枚举的",
};

o.propertyIsEnumerable("enumerableOwn"); // TypeError: o.propertyIsEnumerable is not a function
Object.prototype.propertyIsEnumerable.call(o, "enumerableOwn"); // true
```

或者，你也可以使用 `Object.getOwnPropertyDescriptor()`，它也有助于区分不存在的属性和实际上不可枚举的属性。

```js
const o = {
  \_\_proto\_\_: null,
  enumerableOwn: "是可枚举的",
};

Object.getOwnPropertyDescriptor(o, "enumerableOwn")?.enumerable; // true
Object.getOwnPropertyDescriptor(o, "nonExistent")?.enumerable; // undefined
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-object.prototype.propertyisenumerable |

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

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

参见
--

* 属性的可枚举性和所有权
* `for...in`
* `Object.keys()`
* `Object.defineProperty()`


Object.prototype.hasOwnProperty() - JavaScript

Object.prototype.hasOwnProperty()
=================================

**`hasOwnProperty()`** 方法返回一个布尔值，表示对象自有属性（而不是继承来的属性）中是否具有指定的属性。

**备注：** 在支持 `Object.hasOwn` 的浏览器中，建议使用 `Object.hasOwn()`，而非 `hasOwnProperty()`。

尝试一下
----

语法
--

```js
hasOwnProperty(prop)
```

### 参数

`prop`

要测试的属性的字符串名称或者 Symbol。

### 返回值

如果对象有指定属性作为自有属性，则返回 `true`；否则返回 `false`。

描述
--

如果指定的属性是对象的直接属性——即使值为 `null` 或者 `undefined`，**`hasOwnProperty()`** 方法也会返回 `true`。如果属性是继承的，或者根本没有声明该属性，则该方法返回 `false`。与 `in` 运算符不同的是，该方法不会在对象原型链中检查指定的属性。

该方法可以在*大多数* JavaScript 对象中使用，因为大多数对象都是从 `Object` 派生而来，因此会继承该方法。例如 `Array` 是一个 `Object`，所以你可以使用 `hasOwnProperty()` 方法来检查索引是否存在：

```js
const fruits = ["Apple", "Banana", "Watermelon", "Orange"];
fruits.hasOwnProperty(3); // 返回 true ('Orange')
fruits.hasOwnProperty(4); // 返回 false——未定义
```

如果在对象中重新实现了该方法，或者使用 `Object.create(null)` 创建了对象（这些对象不继承自 `Object.prototype`），则该方法将不可用。下面给出了这些情况的示例。

示例
--

### 使用 hasOwnProperty 测试自有属性是否存在

以下代码展示了如何确定 `example` 对象包含了一个名为 `prop` 的属性。

```js
const example = {};
example.hasOwnProperty("prop"); // 返回 false

example.prop = "exists";
example.hasOwnProperty("prop"); // 返回 true——“prop”已定义

example.prop = null;
example.hasOwnProperty("prop"); // 返回 true——自有属性存在且值为 null

example.prop = undefined;
example.hasOwnProperty("prop"); // 返回 true——自有属性存在且值为 undefined
```

### 直接属性 vs 继承属性

以下示例区分了直接属性和通过原型链继承的属性：

```js
const example = {};
example.prop = "exists";

// `hasOwnProperty` 仅对直接属性返回 true：
example.hasOwnProperty("prop"); // 返回 true
example.hasOwnProperty("toString"); // 返回 false
example.hasOwnProperty("hasOwnProperty"); // 返回 false

// 对于直接或继承的属性，`in` 运算符将返回 true：
"prop" in example; // 返回 true
"toString" in example; // 返回 true
"hasOwnProperty" in example; // 返回 true
```

### 遍历对象的属性

以下示例展示了如何在忽略继承属性的情况下迭代一个对象的可枚举属性。

```js
const buz = {
  fog: "stack",
};

for (const name in buz) {
  if (buz.hasOwnProperty(name)) {
    console.log(`this is fog (${name}) for sure. Value: ${buz[name]}`);
  } else {
    console.log(name); // toString 或其他的方法等
  }
}
```

请注意，`for...in` 循环只迭代可枚举属性：循环中没有发出非可枚举属性并不意味着 `hasOwnProperty` 本身被严格限制在可枚举属性中（和 `Object.getOwnPropertyNames()` 一样）。

### 使用 hasOwnProperty 作为属性名称

JavaScript 并不保护属性名称 `hasOwnProperty`；具有此名称属性的对象可能会返回不正确的结果：

```js
const foo = {
  hasOwnProperty() {
    return false;
  },
  bar: "Here be dragons",
};

foo.hasOwnProperty("bar"); // 该重新实现始终返回 false
```

克服这个问题的推荐方法是，在支持的浏览器中使用 `Object.hasOwn()`。其他替代方案包括使用*外部的* `hasOwnProperty`：

```js
const foo = { bar: "Here be dragons" };

// 使用 Object.hasOwn() 方法——推荐
Object.hasOwn(foo, "bar"); // 返回 true

// 使用 Object 原型中的 hasOwnProperty 属性
Object.prototype.hasOwnProperty.call(foo, "bar"); // 返回 true

// 使用另一个 Object 的 hasOwnProperty，并在“this”设置为 foo 的情况下调用它
({}).hasOwnProperty.call(foo, "bar"); // 返回 true
```

请注意，在前两种情况下，没有新创建的对象。

### 由 Object.create(null) 创建的对象

使用 `Object.create(null)` 创建的对象不从 `Object.prototype` 继承，使得 `hasOwnProperty()` 不可访问。

```js
const foo = Object.create(null);
foo.prop = "exists";
foo.hasOwnProperty("prop"); // Uncaught TypeError: foo.hasOwnProperty is not a function
```

这种情况下的解决方案与上一节相同：使用 `Object.hasOwn()`，否则使用外部对象的 `hasOwnProperty()`。

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-object.prototype.hasownproperty |

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

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

参见
--

* `Object.hasOwn()`
* 属性的可枚举性和所有权
* `Object.getOwnPropertyNames()`
* `for...in`
* `in`
* 继承与原型链


Object.defineProperty() - JavaScript

Object.defineProperty()
=======================

**`Object.defineProperty()`** 静态方法会直接在一个对象上定义一个新属性，或修改其现有属性，并返回此对象。

尝试一下
----

语法
--

```js
Object.defineProperty(obj, prop, descriptor)
```

### 参数

`obj`

要定义属性的对象。

`prop`

一个字符串或 `Symbol`，指定了要定义或修改的属性键。

`descriptor`

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

### 返回值

传入函数的对象，其指定的属性已被添加或修改。

描述
--

`Object.defineProperty()` 允许精确地添加或修改对象上的属性。通过赋值添加的普通属性会在枚举属性时（例如 `for...in`、`Object.keys()` 等）出现，它们的值可以被更改，也可以被删除。此方法允许更改这些额外细节，以使其不同于默认值。默认情况下，使用 `Object.defineProperty()` 添加的属性是不可写、不可枚举和不可配置的。此外，`Object.defineProperty()` 使用 `[[DefineOwnProperty]]` 内部方法，而不是 `[[Set]]`，因此即使属性已经存在，它也不会调用 setter。

对象中存在的属性描述符有两种主要类型：数据描述符和访问器描述符。**数据描述符**是一个具有可写或不可写值的属性。**访问器描述符**是由 getter/setter 函数对描述的属性。描述符只能是这两种类型之一，不能同时为两者。

数据描述符和访问器描述符都是对象。它们共享以下可选键（请注意：在使用 `Object.defineProperty()` 定义属性的情况下，下述所有键都是**默认值**）：

`configurable`

当设置为 `false` 时，

* 该属性的类型不能在数据属性和访问器属性之间更改，且
* 该属性不可被删除，且
* 其描述符的其他属性也不能被更改（但是，如果它是一个可写的数据描述符，则 `value` 可以被更改，`writable` 可以更改为 `false`）。

**默认值为 `false`。**

`enumerable`

当且仅当该属性在对应对象的属性枚举中出现时，值为 `true`。**默认值为 `false`。**

**数据描述符**还具有以下可选键值：

`value`

与属性相关联的值。可以是任何有效的 JavaScript 值（数字、对象、函数等）。**默认值为 `undefined`。**

`writable`

如果与属性相关联的值可以使用赋值运算符更改，则为 `true`。**默认值为 `false`。**

**访问器描述符**还具有以下可选键值：

`get`

用作属性 getter 的函数，如果没有 getter 则为 `undefined`。当访问该属性时，将不带参地调用此函数，并将 `this` 设置为通过该属性访问的对象（因为可能存在继承关系，这可能不是定义该属性的对象）。返回值将被用作该属性的值。**默认值为 `undefined`。**

`set`

用作属性 setter 的函数，如果没有 setter 则为 `undefined`。当该属性被赋值时，将调用此函数，并带有一个参数（要赋给该属性的值），并将 `this` 设置为通过该属性分配的对象。**默认值为 `undefined`。**

如果描述符没有 `value`、`writable`、`get` 和 `set` 键中的任何一个，它将被视为数据描述符。如果描述符同时具有 [`value` 或 `writable`] 和 [`get` 或 `set`] 键，则会抛出异常。

这些属性不一定是描述符本身的属性。继承的属性也会被考虑在内。为了确保这些默认值得到保留，你可以预先冻结描述符对象原型链中的现有对象，明确指定所有选项，或使用 `Object.create(null)` 指向 `null`。

```js
const obj = {};
// 1. 使用 null 原型：没有继承的属性
const descriptor = Object.create(null);
descriptor.value = "static";

// 默认情况下，它们不可枚举、不可配置、不可写
Object.defineProperty(obj, "key", descriptor);

// 2. 使用一个包含所有属性的临时对象字面量来明确其属性
Object.defineProperty(obj, "key2", {
  enumerable: false,
  configurable: false,
  writable: false,
  value: "static",
});

// 3. 重复利用同一对象
function withValue(value) {
  const d =
    withValue.d ||
    (withValue.d = {
      enumerable: false,
      writable: false,
      configurable: false,
      value,
    });

  // 避免重复赋值
  if (d.value !== value) d.value = value;

  return d;
}
// 然后
Object.defineProperty(obj, "key", withValue("static"));

// 如果 freeze 可用，防止添加或删除对象原型属性
// （value、get、set、enumerable、writable、configurable）
(Object.freeze || Object)(Object.prototype);
```

当属性已经存在时，`Object.defineProperty()` 会尝试根据描述符和属性的当前配置修改属性。

如果旧描述符的 `configurable` 特性被设置为 `false`，则该属性被称为*不可配置的*。无法更改不可配置的访问器属性的任何特性，也不能将其在数据类型和访问器类型之间切换。对于具有 `writable: true` 的数据属性，可以修改其值并将 `writable` 特性从 `true` 改为 `false`。当试图更改不可配置的属性（除非允许更改 `value` 和 `writable`）时，会抛出 `TypeError`，除非在数据属性上定义一个与原始值相同的值。

当当前属性是可配置的时，将特性设置为 `undefined` 可以有效地删除它。例如，如果 `o.k` 是一个访问器属性，`Object.defineProperty(o, "k", { set: undefined })` 将删除 setter，使 `k` 只有 getter 并变成只读的。如果新描述符中缺少一个特性，则会保留旧描述符该特性的值（不会被隐式重新设置为 `undefined`）。通过提供不同类型的描述符，可以在数据属性和访问器属性之间切换。例如，如果新描述符是数据描述符（带有 `value` 或 `writable`），则原始描述符的 `get` 和 `set` 属性都将被删除。

示例
--

### 创建属性

当对象中不存在指定的属性时，`Object.defineProperty()` 将根据描述符创建一个新的属性。描述符中的字段可以省略，省略的字段将使用默认值。

```js
const o = {}; // 创建一个新对象

// 通过 defineProperty 使用数据描述符添加对象属性的示例
Object.defineProperty(o, "a", {
  value: 37,
  writable: true,
  enumerable: true,
  configurable: true,
});
// 'a' 属性存在于对象 o 中，其值为 37

// 通过 defineProperty 使用访问器属性描述符添加对象属性的示例
let bValue = 38;
Object.defineProperty(o, "b", {
  get() {
    return bValue;
  },
  set(newValue) {
    bValue = newValue;
  },
  enumerable: true,
  configurable: true,
});
o.b; // 38
// 'b' 属性存在于对象 o 中，其值为 38。
// o.b 的值现在始终与 bValue 相同，除非重新定义了 o.b。

// 数据描述符和访问器描述符不能混合使用
Object.defineProperty(o, "conflict", {
  value: 0x9f91102,
  get() {
    return 0xdeadbeef;
  },
});
// 抛出错误 TypeError: value appears only in data descriptors, get appears only in accessor descriptors
```

### 修改属性

当修改已存在的属性时，操作的结果取决于当前属性的配置，可能会成功、不执行任何操作，或抛出一个 `TypeError` 异常。

#### Writable 特性

当 `writable` 特性设置为 `false` 时，该属性被称为“不可写的”。它不能被重新赋值。尝试对一个不可写的属性进行写入不会改变它，在严格模式下还会导致错误。

```js
const o = {}; // 创建一个新对象

Object.defineProperty(o, "a", {
  value: 37,
  writable: false,
});

console.log(o.a); // 37
o.a = 25; // 不会抛出错误
// （在严格模式下，即使值相同，它也会抛出异常）
console.log(o.a); // 37；赋值不会成功

// 严格模式
(() => {
  "use strict";
  const o = {};
  Object.defineProperty(o, "b", {
    value: 2,
    writable: false,
  });
  o.b = 3; // 抛出 TypeError: "b" is read-only
  return o.b; // 如果没有上一行的话，会返回 2
})();
```

#### Enumerable 特性

`enumerable` 特性定义了属性是否可以被 `Object.assign()` 或展开运算符所考虑。对于非 `Symbol` 属性，它还定义了属性是否会在 `for...in` 循环和 `Object.keys()` 中显示。更多信息，请参见属性的枚举性和所有权。

```js
const o = {};
Object.defineProperty(o, "a", {
  value: 1,
  enumerable: true,
});
Object.defineProperty(o, "b", {
  value: 2,
  enumerable: false,
});
Object.defineProperty(o, "c", {
  value: 3,
}); // enumerable 默认为 false
o.d = 4; // 通过赋值创建属性时 enumerable 默认为 true
Object.defineProperty(o, Symbol.for("e"), {
  value: 5,
  enumerable: true,
});
Object.defineProperty(o, Symbol.for("f"), {
  value: 6,
  enumerable: false,
});

for (const i in o) {
  console.log(i);
}
// 打印 'a' 和 'd'（总是按这个顺序）

Object.keys(o); // ['a', 'd']

o.propertyIsEnumerable("a"); // true
o.propertyIsEnumerable("b"); // false
o.propertyIsEnumerable("c"); // false
o.propertyIsEnumerable("d"); // true
o.propertyIsEnumerable(Symbol.for("e")); // true
o.propertyIsEnumerable(Symbol.for("f")); // false

const p = { ...o };
p.a; // 1
p.b; // undefined
p.c; // undefined
p.d; // 4
p[Symbol.for("e")]; // 5
p[Symbol.for("f")]; // undefined
```

#### Configurable 特性

`configurable` 特性控制属性是否可以从对象中删除以及其特性（除了 `value` 和 `writable`）是否可以更改。

以下示例演示了一个不可配置的访问器属性。

```js
const o = {};
Object.defineProperty(o, "a", {
  get() {
    return 1;
  },
  configurable: false,
});

Object.defineProperty(o, "a", {
  configurable: true,
}); // 抛出 TypeError
Object.defineProperty(o, "a", {
  enumerable: true,
}); // 抛出 TypeError
Object.defineProperty(o, "a", {
  set() {},
}); // 抛出 TypeError（set 在之前未定义）
Object.defineProperty(o, "a", {
  get() {
    return 1;
  },
}); // 抛出 TypeError
// （即使新的 get 做的事情完全相同）
Object.defineProperty(o, "a", {
  value: 12,
}); // 抛出 TypeError
// ‘value’只有在 writable 为 true 时才可以被修改

console.log(o.a); // 1
delete o.a; // 无法删除；严格模式下会抛出错误
console.log(o.a); // 1
```

如果 `o.a` 的 `configurable` 特性为 `true`，则不会抛出任何错误，并且该属性会在最后被删除。

以下示例说明了一个不可配置但可写的数据属性。该属性的 `value` 仍然可以被更改，`writable` 也仍然可以从 `true` 切换到 `false`。

```js
const o = {};
Object.defineProperty(o, "b", {
  writable: true,
  configurable: false,
});
console.log(o.b); // undefined
Object.defineProperty(o, "b", {
  value: 1,
}); // 即使 configurable 为 false，因为对象是可写的，我们仍然可以替换属性的值。
console.log(o.b); // 1
o.b = 2; // 我们也可以使用赋值运算符来更改属性的值
console.log(o.b); // 2
// 切换属性的可写性
Object.defineProperty(o, "b", {
  writable: false,
});
Object.defineProperty(o, "b", {
  value: 1,
}); // TypeError: because the property is neither writable nor configurable, it cannot be modified
// 此时，无法再次修改属性 'b' 或者恢复它的可写性。
```

这个示例演示了一个可配置但不可写的数据属性。该属性的 `value` 仍然可以使用 `defineProperty` 进行替换（但不能使用赋值运算符），并且 `writable` 特性仍然可以切换。

```js
const o = {};
Object.defineProperty(o, "b", {
  writable: false,
  configurable: true,
});
Object.defineProperty(o, "b", {
  value: 1,
}); // 我们可以使用 defineProperty 方法替换属性的值
console.log(o.b); // 1
o.b = 2; // 在严格模式下抛出 TypeError：cannot change a non-writable property's value with assignment
```

这个示例演示了一个不可配置且不可写的数据属性。无法更新该属性的任何特性，包括它的 `value` 值。

```js
const o = {};
Object.defineProperty(o, "b", {
  writable: false,
  configurable: false,
});
Object.defineProperty(o, "b", {
  value: 1,
}); // TypeError: the property cannot be modified because it is neither writable nor configurable.
```

### 添加多个属性和默认值

考虑属性默认值应用的方式是非常重要的。通常，在使用属性访问器分配一个值和使用 `Object.defineProperty()` 之间存在差异，如下面的示例所示。

```js
const o = {};

o.a = 1;
// 等价于：
Object.defineProperty(o, "a", {
  value: 1,
  writable: true,
  configurable: true,
  enumerable: true,
});

// 另一种情况
Object.defineProperty(o, "a", { value: 1 });
// 等价于：
Object.defineProperty(o, "a", {
  value: 1,
  writable: false,
  configurable: false,
  enumerable: false,
});
```

### 自定义 setter 和 getter

下面的例子展示了如何实现一个自存档对象。当设置 `temperature` 属性时，`archive` 数组会收到日志条目。

```js
function Archiver() {
  let temperature = null;
  const archive = [];

  Object.defineProperty(this, "temperature", {
    get() {
      console.log("get!");
      return temperature;
    },
    set(value) {
      temperature = value;
      archive.push({ val: temperature });
    },
  });

  this.getArchive = () => archive;
}

const arc = new Archiver();
arc.temperature; // 'get!'
arc.temperature = 11;
arc.temperature = 13;
arc.getArchive(); // [{ val: 11 }, { val: 13 }]
```

下面这个例子中，getter 总是会返回一个相同的值。

```js
const pattern = {
  get() {
    return "我总是返回这个字符串，无论你的赋值是什么";
  },
  set() {
    this.myname = "这是我名称的字符串";
  },
};

function TestDefineSetAndGet() {
  Object.defineProperty(this, "myproperty", pattern);
}

const instance = new TestDefineSetAndGet();
instance.myproperty = "test";
console.log(instance.myproperty);
// 我总是返回这个字符串，无论你的赋值是什么

console.log(instance.myname); // 这是我名称的字符串
```

### 继承属性

如果访问器属性被继承，它的 `get` 和 `set` 方法会在派生对象的属性被访问或者修改时被调用。如果这些方法用一个变量存值，该值会被所有对象共享。

```js
function MyClass() {}

let value;
Object.defineProperty(MyClass.prototype, "x", {
  get() {
    return value;
  },
  set(x) {
    value = x;
  },
});

const a = new MyClass();
const b = new MyClass();
a.x = 1;
console.log(b.x); // 1
```

这可以通过将值存储在另一个属性中解决。在 `get` 和 `set` 方法中，`this` 指向某个被访问和修改属性的对象。

```js
function MyClass() {}

Object.defineProperty(MyClass.prototype, "x", {
  get() {
    return this.storedX;
  },
  set(x) {
    this.storedX = x;
  },
});

const a = new MyClass();
const b = new MyClass();
a.x = 1;
console.log(b.x); // undefined
```

与访问器属性不同，数据属性始终在对象自身上设置，而不是一个原型。然而，如果一个不可写的属性被继承，它仍然可以防止修改对象的属性。

```js
function MyClass() {}

MyClass.prototype.x = 1;
Object.defineProperty(MyClass.prototype, "y", {
  writable: false,
  value: 1,
});

const a = new MyClass();
a.x = 2;
console.log(a.x); // 2
console.log(MyClass.prototype.x); // 1
a.y = 2; // 没有作用；严格模式下会报错
console.log(a.y); // 1
console.log(MyClass.prototype.y); // 1
```

规范
--

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

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

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

参见
--

* 属性的可枚举性和所有权
* `Object.defineProperties()`
* `Object.prototype.propertyIsEnumerable()`
* `Object.getOwnPropertyDescriptor()`
* `get`
* `set`
* `Object.create()`
* `Reflect.defineProperty()`


Object.keys() - JavaScript

Object.keys()
=============

**`Object.keys()`** 静态方法返回一个由给定对象自身的可枚举的字符串键属性名组成的数组。

尝试一下
----

语法
--

```js
Object.keys(obj)
```

### 参数

`obj`

一个对象。

### 返回值

一个由给定对象自身可枚举的字符串键属性键组成的数组。

描述
--

`Object.keys()` 返回一个数组，其元素是字符串，对应于直接在对象上找到的可枚举的字符串键属性名。这与使用 `for...in` 循环迭代相同，只是 `for...in` 循环还会枚举原型链中的属性。`Object.keys()` 返回的数组顺序和与 `for...in` 循环提供的顺序相同。

如果你需要属性的值，请使用 `Object.values()`。如果你同时需要属性的键和值，请使用 `Object.entries()`。

示例
--

### 使用 Object.keys()

```js
// 简单数组
const arr = ["a", "b", "c"];
console.log(Object.keys(arr)); // ['0', '1', '2']

// 类数组对象
const obj = { 0: "a", 1: "b", 2: "c" };
console.log(Object.keys(obj)); // ['0', '1', '2']

// 键的顺序随机的类数组对象
const anObj = { 100: "a", 2: "b", 7: "c" };
console.log(Object.keys(anObj)); // ['2', '7', '100']

// getFoo 是一个不可枚举的属性
const myObj = Object.create(
  {},
  {
    getFoo: {
      value() {
        return this.foo;
      },
    },
  },
);
myObj.foo = 1;
console.log(Object.keys(myObj)); // ['foo']
```

如果你想要*所有*以字符串为键的自有属性，包括不可枚举的属性，参见 `Object.getOwnPropertyNames()`。

### 在基本类型中使用 Object.keys()

非对象参数会强制转换为对象。`undefined` 和 `null` 不能被强制转换为对象，会立即抛出 `TypeError`。只有字符串可以有自己的可枚举属性，而其他所有基本类型都返回一个空数组。

```js
// 字符串具有索引作为可枚举的自有属性
console.log(Object.keys("foo")); // ['0', '1', '2']

// 其他基本类型（除了 undefined 和 null）没有自有属性
console.log(Object.keys(100)); // []
```

**备注：** 在 ES5 中，将一个非对象传递给 `Object.keys()` 会抛出一个 `TypeError`。

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-object.keys |

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

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

参见
--

* `core-js` 中 `Object.keys` 的 Polyfill
* 属性的可枚举性和所有权
* `Object.entries()`
* `Object.values()`
* `Object.prototype.propertyIsEnumerable()`
* `Object.create()`
* `Object.getOwnPropertyNames()`
* `Map.prototype.keys()`


Object.prototype.__defineGetter__() - JavaScript

Object.prototype.\_\_defineGetter\_\_()
=======================================

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

**备注：** 此特性已弃用，建议使用对象初始化语法或 `Object.defineProperty()` API 来定义 getter。该方法的行为只针对 Web 兼容性进行了规定，在任何平台上都不需要实现该方法。它可能无法在所有地方正常工作。

**`__defineGetter__()`** 方法将一个对象的属性绑定到一个函数上，当该属性被访问时，该函数将被调用。

语法
--

```js
\_\_defineGetter\_\_(prop, func)
```

### 参数

`prop`

`func` 所绑定的 getter 函数所对应的属性名称，以字符串形式表示。

`func`

绑定到指定属性查询的函数。

### 返回值

`undefined`。

### 异常

`TypeError`

如果 `func` 不是函数，则抛出。

描述
--

所有继承自 `Object.prototype` 的对象（也就是除了 `null` 原型对象的对象）都会继承 `__defineGetter__()` 方法。该方法允许在一个已存在的对象上定义一个 getter。这相当于使用 `Object.defineProperty(obj, prop, { get: func, configurable: true, enumerable: true })` 方法，这意味着该属性是可枚举和可配置的，而且如果存在，则保留任何现有的 setter。

`__defineGetter__()` 在规范中被定义为“可选规范”，这意味着不需要任何实现来实现它。然而，所有主要的浏览器都实现了它，并且由于它的持续使用，它不太可能被删除。如果一个浏览器实现了 `__defineGetter__()`，那么它还需要实现 `__lookupGetter__()`、`__lookupSetter__()` 和 `__defineSetter__()` 方法。

示例
--

### 使用 \_\_defineGetter\_\_()

```js
const o = {};
o.\_\_defineGetter\_\_("gimmeFive", function () {
  return 5;
});
console.log(o.gimmeFive); // 5
```

### 以标准方式定义 getter 属性

在对象首次初始化时，你可以使用 `get` 语法来定义一个 getter。

```js
const o = {
  get gimmeFive() {
    return 5;
  },
};
console.log(o.gimmeFive); // 5
```

你也可以使用 `Object.defineProperty()` 在对象创建后为其定义一个 getter。与 `__defineGetter__()` 相比，此方法允许你控制 getter 的可枚举性和可配置性，以及定义 Symbol 属性。`Object.defineProperty()` 方法也适用于 `null` 原型对象，因为他们没有继承 `Object.prototype`，所以没有 `__defineGetter__()` 方法。

```js
const o = {};
Object.defineProperty(o, "gimmeFive", {
  get() {
    return 5;
  },
  configurable: true,
  enumerable: true,
});
console.log(o.gimmeFive); // 5
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-object.prototype.\_\_defineGetter\_\_ |

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

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

参见
--

* `core-js` 中 `Object.prototype.__defineGetter__` 的 polyfill
* `Object.prototype.__defineSetter__()`
* `get` 语法
* `Object.defineProperty()`
* `Object.prototype.__lookupGetter__()`
* `Object.prototype.__lookupSetter__()`
* JS 指南：定义 Getter 和 Setter
* [博文] \_\_defineGetter\_\_ 和 \_\_defineSetter\_\_ 已被弃用
* Firefox bug 647423


Object.defineProperties() - JavaScript

Object.defineProperties()
=========================

**`Object.defineProperties()`** 静态方法直接在一个对象上定义新的属性或修改现有属性，并返回该对象。

尝试一下
----

语法
--

```js
Object.defineProperties(obj, props)
```

### 参数

`obj`

在其上定义或修改属性的对象。

`props`

一个对象，其中每个键表示要定义或修改的属性的名称，每个值是描述该属性的对象。在 `props` 中的每个值必须是且只能是数据描述符或访问器描述符之一；不能同时为两者（更多详细信息，请参见`Object.defineProperty()`）。

数据描述符和访问器描述符可以包含以下可选键：

`configurable`

如果此属性描述符的类型可以更改并且属性可以从相应的对象中删除，则为 `true`。**默认为 `false`。**

`enumerable`

如果此属性在枚举相应对象的属性时应显示出来，则为 `true`。**默认为 `false`。**

数据描述符还具有以下可选键：

`value`

与属性关联的值。可以是任何有效的 JavaScript 值（数字、对象、函数等）。**默认为 `undefined`。**

`writable`

如果与属性关联的值可以使用赋值运算符更改，则为 `true`。**默认为 `false`。**

访问器描述符还具有以下可选键：

`get`

作为该属性的 getter 函数，如果没有 getter 则为 `undefined`。函数返回值将被用作属性的值。**默认为 `undefined`。**

`set`

作为该属性的 setter 函数，如果没有 setter 则为 `undefined`。该函数将只接收一个参数，即被分配给属性的新值。**默认为 `undefined`。**

如果一个属性描述符没有 `value`、`writable`、`get`、`set` 键中的任何一个，那么它被视为一个数据描述符。如果一个属性描述符同时具有 `value` 或 `writable` 和 `get` 或 `set` 键中的任意一个组合，就会抛出异常。

### 返回值

传递给函数的对象。

示例
--

### 使用 Object.defineProperties

```js
const obj = {};
Object.defineProperties(obj, {
  property1: {
    value: true,
    writable: true,
  },
  property2: {
    value: "Hello",
    writable: false,
  },
  // 等等……
});
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-object.defineproperties |

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

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

参见
--

* `core-js` 中 `Object.defineProperties` 的 polyfill
* `Object.defineProperty()`
* `Object.keys()`
* 属性的可枚举性和所有权


Object.preventExtensions() - JavaScript

Object.preventExtensions()
==========================

**`Object.preventExtensions()`** 静态方法可以防止新属性被添加到对象中（即防止该对象被扩展）。它还可以防止对象的原型被重新指定。

尝试一下
----

语法
--

```js
Object.preventExtensions(obj)
```

### 参数

`obj`

将要变得不可扩展的对象。

### 返回值

已经不可扩展的对象。

描述
--

如果一个对象可以添加新的属性，则这个对象是可扩展的。`Object.preventExtensions()` 将对象标记为不再可扩展，这样它将永远不会具有它被标记为不可扩展时持有的属性之外的属性。注意，一般来说，不可扩展对象的属性仍然可以被*删除*。尝试向不可扩展对象添加新属性将静默失败，或在严格模式中抛出 `TypeError`。

与 `Object.seal()` 和 `Object.freeze()` 不同，`Object.preventExtensions()` 调用了内在的 JavaScript 行为，不能用几个其他操作的组合替代。它还有它的 `Reflect` 对应方法（仅存在于内部操作中），`Reflect.preventExtensions()`。

`Object.preventExtensions()` 只能防止添加自有属性。但其对象类型的原型依然可以添加新的属性。

该方法使得目标对象的 `[[Prototype]]` 不可变；任何重新赋值 `[[Prototype]]` 操作都会抛出 `TypeError` 。这种行为只针对内部的 `[[Prototype]]` 属性，目标对象的其他属性将保持可变。

一旦将对象变为不可扩展的对象，就再也不能使其可扩展。

示例
--

### 使用 Object.preventExtensions()

```js
// Object.preventExtensions 将原对象变的不可扩展，并且返回原对象。
const obj = {};
const obj2 = Object.preventExtensions(obj);
obj === obj2; // true

// 字面量方式定义的对象默认是可扩展的。
const empty = {};
Object.isExtensible(empty); // true

// 可以将其改变为不可扩展的。
Object.preventExtensions(empty);
Object.isExtensible(empty); // false

// 使用 Object.defineProperty 方法为一个不可扩展的对象添加新属性会抛出异常。
const nonExtensible = { removable: true };
Object.preventExtensions(nonExtensible);
Object.defineProperty(nonExtensible, "new", {
  value: 8675309,
}); // 抛出 TypeError

// 在严格模式中，为一个不可扩展对象的新属性赋值会抛出 TypeError 异常。
function fail() {
  "use strict";
  // 抛出 TypeError
  nonExtensible.newProperty = "FAIL";
}
fail();
```

不可扩展对象的原型是不可变的：

```js
const fixed = Object.preventExtensions({});
// 抛出 TypeError
fixed.__proto__ = { oh: "hai" };
```

非对象参数
-----

在 ES5 中，如果提供给该方法的参数不是一个对象类型（而是原始类型），将抛出 `TypeError`。在 ES2015 中，非对象参数将被原样返回，因为原始类型是定义上不可变的。

```js
Object.preventExtensions(1);
// TypeError: 1 is not an object (ES5 code)

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

规范
--

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

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

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

参见
--

* `Object.isExtensible()`
* `Object.seal()`
* `Object.isSealed()`
* `Object.freeze()`
* `Object.isFrozen()`
* `Reflect.preventExtensions()`


Object.prototype.valueOf() - JavaScript

Object.prototype.valueOf()
==========================

`Object` 实例的 **`valueOf()`** 方法将 `this` 值转换成对象。该方法旨在被派生对象重写，以实现自定义类型转换逻辑。

尝试一下
----

语法
--

```js
valueOf()
```

### 参数

无。

### 返回值

转换成对象的 `this` 值。

**备注：** 为了使 `valueOf` 在类型转换过程中有用，它必须返回一个基本类型值。因为所有的基本类型都有自己的 `valueOf()` 方法，所以调用 `aPrimitiveValue.valueOf()` 通常不会调用 `Object.prototype.valueOf()`。

描述
--

JavaScript 调用 `valueOf` 方法来将对象转换成基本类型值。你很少需要自己调用 `valueOf` 方法；当遇到需要基本类型值的对象时，JavaScript 会自动的调用该方法。

强制数字类型转换和强制基本类型转换优先会调用该方法，而强制字符串转换会优先调用 `toString()`，并且 `toString()` 很可能返回字符串值（甚至对于 `Object.prototype.toString()` 基本实现也是如此），因此在这种情况下不会调用 `valueOf()`。

从 `Object.prototype` 继承的所有对象（即，除了`null` 原型对象之外的所有对象）都继承了 `toString()` 方法。`Object.prototype.valueOf()` 的基本实现被有意设计为无用的：返回一个对象；其返回值将永远不会被任何基本类型转换算法使用。许多内置对象重写此方法以返回适当的基本类型值。创建自定义对象时，可以重写 `valueOf()` 来调用自定义方法，以便将自定义对象转换为基本类型值。通常，`valueOf()` 用于返回对对象具有意义的值——与 `toString()` 不同，它不需要字符串。或者，你可以添加一个 `@@toPrimitive` 方法，该方法允许对转换过程进行更多控制，并且对于任何类型的转换，始终优先于 `valueOf` 或 `toString`。

示例
--

### 使用 valueOf()

基本的 `valueOf()` 方法返回 `this` 值本身，如果尚未转换为对象，则转换成对象。因此，任何基本类型转换算法都不会使用它的返回值。

```js
const obj = { foo: 1 };
console.log(obj.valueOf() === obj); // true

console.log(Object.prototype.valueOf.call("primitive"));
// [String: 'primitive']（一个包装对象）
```

### 重写自定义对象的 valueOf

你可以创建一个要调用的函数来代替默认的 `valueOf` 方法。你的函数不应该接受任何参数，因为在类型转换期间调用它时不会传递任何参数。

例如，你可以将 `valueOf` 方法添加到自定义类 `Box` 中。

```js
class Box {
  #value;
  constructor(value) {
    this.#value = value;
  }
  valueOf() {
    return this.#value;
  }
}
```

有了前面的代码，任何时候在将 `Box` 类型的对象表示为基本类型值（但不是特定的字符串）的上下文中使用时，JavaScript 都会自动调用前面代码中定义的函数。

```js
const box = new Box(123);
console.log(box + 456); // 579
console.log(box == 123); // true
```

对象的 `valueOf` 方法通常由 JavaScript 调用，但你可以按照如下方式自己调用它：

```js
box.valueOf();
```

### 对对象使用一元加运算符

一元加（+）对其操作数进行强制数字转换，对于大多数没有 `@@toPrimitive` 的对象，这意味着调用其 `valueOf()`。但是，如果对象没有自定义的 `valueOf()` 方法，则基本实现将导致忽略 `valueOf()`，而使用 `toString()` 的返回值。

```js
+new Date(); // 当前时间戳；与 new Date().getTime() 相同
+{}; // NaN（toString() 返回 "[object Object]"）
+[]; // 0（toString() 返回一个空的字符串列表）
+[1]; // 1（toString() 返回 "1"）
+[1, 2]; // NaN（toString() 返回 "1,2"）
+new Set([1]); // NaN（toString() 返回 "[object Set]"）
+{ valueOf: () => 42 }; // 42
```

规范
--

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

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

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

参见
--

* `Object.prototype.toString()`
* `parseInt()`
* `Symbol.toPrimitive`


Object.assign() - JavaScript

Object.assign()
===============

**`Object.assign()`** 静态方法将一个或者多个*源对象*中所有可枚举的自有属性复制到*目标对象*，并返回修改后的目标对象。

尝试一下
----

语法
--

```js
Object.assign(target, ...sources)
```

### 参数

`target`

需要应用源对象属性的目标对象，修改后将作为返回值。

`sources`

一个或多个包含要应用的属性的源对象。

### 返回值

修改后的目标对象。

描述
--

如果目标对象与源对象具有相同的键（属性名），则目标对象中的属性将被源对象中的属性覆盖，后面的源对象的属性将类似地覆盖前面的源对象的同名属性。

`Object.assign()` 方法只会拷贝源对象*可枚举的*的*自有属性*到目标对象。该方法在源对象上使用 `[[Get]]`，在目标对象上使用 `[[Set]]`，因此它会调用 getter 和 setter。故它对属性进行*赋值*，而不仅仅是复制或定义新的属性。如果合并源对象包含 getter 的新属性到原型中，则可能不适合使用此方法。

如果要将属性定义（包括它们的可枚举性）复制到原型中，则应改用 `Object.getOwnPropertyDescriptor()` 和 `Object.defineProperty()` 方法。

`字符串`和 `Symbol` 类型属性都会被复制。

如果赋值期间出错，例如如果属性不可写，则会抛出 `TypeError`；如果在抛出异常之前已经添加了一些属性，则这些属性会被保留，而 `target` 对象也会被修改。

**备注：** `Object.assign()` 不会在源对象值为 `null` 或 `undefined` 时抛出错误。

示例
--

### 复制对象

```js
const obj = { a: 1 };
const copy = Object.assign({}, obj);
console.log(copy); // { a: 1 }
```

### 深拷贝问题

针对深拷贝，需要使用其他办法，因为 `Object.assign()` 只复制属性值。

假如源对象是一个对象的引用，它仅仅会复制其引用值。

```js
const obj1 = { a: 0, b: { c: 0 } };
const obj2 = Object.assign({}, obj1);
console.log(obj2); // { a: 0, b: { c: 0 } }

obj1.a = 1;
console.log(obj1); // { a: 1, b: { c: 0 } }
console.log(obj2); // { a: 0, b: { c: 0 } }

obj2.a = 2;
console.log(obj1); // { a: 1, b: { c: 0 } }
console.log(obj2); // { a: 2, b: { c: 0 } }

obj2.b.c = 3;
console.log(obj1); // { a: 1, b: { c: 3 } }
console.log(obj2); // { a: 2, b: { c: 3 } }

// 深拷贝
const obj3 = { a: 0, b: { c: 0 } };
const obj4 = JSON.parse(JSON.stringify(obj3));
obj3.a = 4;
obj3.b.c = 4;
console.log(obj4); // { a: 0, b: { c: 0 } }
```

### 合并对象

```js
const o1 = { a: 1 };
const o2 = { b: 2 };
const o3 = { c: 3 };

const obj = Object.assign(o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 }
console.log(o1); // { a: 1, b: 2, c: 3 }，目标对象本身发生了变化
```

### 合并具有相同属性的对象

```js
const o1 = { a: 1, b: 1, c: 1 };
const o2 = { b: 2, c: 2 };
const o3 = { c: 3 };

const obj = Object.assign({}, o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 }
```

属性会被后续参数中具有相同属性的其他对象覆盖。

### 拷贝 Symbol 类型属性

```js
const o1 = { a: 1 };
const o2 = { [Symbol("foo")]: 2 };

const obj = Object.assign({}, o1, o2);
console.log(obj); // { a : 1, [Symbol("foo")]: 2 } (cf. bug 1207182 on Firefox)
Object.getOwnPropertySymbols(obj); // [Symbol(foo)]
```

### 原型链上的属性和不可枚举的属性不能被复制

```js
const obj = Object.create(
  // foo 在 obj 的原型链上
  { foo: 1 },
  {
    bar: {
      value: 2, // bar 是不可枚举的属性
    },
    baz: {
      value: 3,
      enumerable: true, // baz 是可枚举的自有属性
    },
  },
);

const copy = Object.assign({}, obj);
console.log(copy); // { baz: 3 }
```

### 基本类型会被封装为对象

```js
const v1 = "abc";
const v2 = true;
const v3 = 10;
const v4 = Symbol("foo");

const obj = Object.assign({}, v1, null, v2, undefined, v3, v4);
// 基本类型将被封装，null 和 undefined 将被忽略。
// 注意，只有字符串封装对象才拥有可枚举的自有属性。
console.log(obj); // { "0": "a", "1": "b", "2": "c" }
```

### 异常会中断后续的复制

```js
const target = Object.defineProperty({}, "foo", {
  value: 1,
  writable: false,
}); // target.foo 是一个只读属性

Object.assign(target, { bar: 2 }, { foo2: 3, foo: 3, foo3: 3 }, { baz: 4 });
// TypeError: "foo" is read-only
// 这个异常会在给 target.foo 赋值的时候抛出

console.log(target.bar); // 2，第一个源对象成功复制。
console.log(target.foo2); // 3，第二个源对象的第一个属性也成功复制。
console.log(target.foo); // 1，异常在这里被抛出
console.log(target.foo3); // undefined，属性赋值已经结束，foo3 不会被复制
console.log(target.baz); // undefined，第三个源对象也不会被复制
```

### 拷贝访问器

```js
const obj = {
  foo: 1,
  get bar() {
    return 2;
  },
};

let copy = Object.assign({}, obj);
console.log(copy);
// { foo: 1, bar: 2 }
// copy.bar 的值是 obj.bar 的 getter 的返回值。

// 这是一个将完整描述符复制的赋值函数
function completeAssign(target, ...sources) {
  sources.forEach((source) => {
    const descriptors = Object.keys(source).reduce((descriptors, key) => {
      descriptors[key] = Object.getOwnPropertyDescriptor(source, key);
      return descriptors;
    }, {});

    // 默认情况下，Object.assign 也会复制可枚举的 Symbol 属性
    Object.getOwnPropertySymbols(source).forEach((sym) => {
      const descriptor = Object.getOwnPropertyDescriptor(source, sym);
      if (descriptor.enumerable) {
        descriptors[sym] = descriptor;
      }
    });
    Object.defineProperties(target, descriptors);
  });
  return target;
}

copy = completeAssign({}, obj);
console.log(copy);
// { foo:1, get bar() { return 2 } }
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-object.assign |

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

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

参见
--

* `core-js` 中 `Object.assign` 的 polyfill
* `Object.defineProperties()`
* 属性的可枚举性和所有权
* 构造字面量对象时使用展开语法


Object.getOwnPropertyNames() - JavaScript

Object.getOwnPropertyNames()
============================

**`Object.getOwnPropertyNames()`** 静态方法返回一个数组，其包含给定对象中所有自有属性（包括不可枚举属性，但不包括使用 symbol 值作为名称的属性）。

尝试一下
----

语法
--

```js
Object.getOwnPropertyNames(obj)
```

### 参数

`obj`

一个对象，其自有的可枚举和不可枚举属性的名称被返回。

### 返回值

在给定对象上找到的自有属性对应的字符串数组。

描述
--

`Object.getOwnPropertyNames()` 返回一个数组，其元素是与给定对象 `obj` 直接关联的可枚举和不可枚举属性对应的字符串。数组中可枚举属性的顺序与使用 `for...in` 循环（或 `Object.keys()`）遍历对象属性时所暴露的顺序一致。对象的非负整数键（包括可枚举和不可枚举的）首先按升序添加到数组中，然后是按插入顺序排列的字符串键。

在 ES5 中，如果该方法的参数不是一个对象（而是基本类型值），则会导致 `TypeError`。在 ES2015 中，非对象参数会被强制转换为对象。

```js
Object.getOwnPropertyNames("foo");
// TypeError: "foo" is not an object (ES5 code)

Object.getOwnPropertyNames("foo");
// ["0", "1", "2", "length"] (ES2015 code)
```

示例
--

### 使用 Object.getOwnPropertyNames()

```js
const arr = ["a", "b", "c"];
console.log(Object.getOwnPropertyNames(arr).sort());
// ["0", "1", "2", "length"]

// 类数组对象
const obj = { 0: "a", 1: "b", 2: "c" };
console.log(Object.getOwnPropertyNames(obj).sort());
// ["0", "1", "2"]

Object.getOwnPropertyNames(obj).forEach((val, idx, array) => {
  console.log(`${val} -> ${obj[val]}`);
});
// 0 -> a
// 1 -> b
// 2 -> c

// 不可枚举属性
const myObj = Object.create(
  {},
  {
    getFoo: {
      value() {
        return this.foo;
      },
      enumerable: false,
    },
  },
);
myObj.foo = 1;

console.log(Object.getOwnPropertyNames(myObj).sort()); // ["foo", "getFoo"]
```

如果你只想获取可枚举属性，请参见 `Object.keys()` 或使用 `for...in` 循环（请注意，这也将返回对象原型链中找到的可枚举属性，除非使用 `hasOwn()` 过滤）。

原型链上的属性不会被列出：

```js
function ParentClass() {}
ParentClass.prototype.inheritedMethod = function () {};

function ChildClass() {
  this.prop = 5;
  this.method = function () {};
}
ChildClass.prototype = new ParentClass();
ChildClass.prototype.prototypeMethod = function () {};

console.log(Object.getOwnPropertyNames(new ChildClass()));
// ["prop", "method"]
```

### 只获取不可枚举的属性

这个方法使用 `Array.prototype.filter()` 函数从所有键（使用 `Object.getOwnPropertyNames()` 获得）的列表中过滤可枚举键（使用 `Object.keys()` 获得），从而仅以不可枚举键作为输出。

```js
const target = myObject;
const enumAndNonenum = Object.getOwnPropertyNames(target);
const enumOnly = new Set(Object.keys(target));
const nonenumOnly = enumAndNonenum.filter((key) => !enumOnly.has(key));

console.log(nonenumOnly);
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-object.getownpropertynames |

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

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

参见
--

* `core-js` 中 `Object.getOwnPropertyNames` 的 polyfill
* 属性的可枚举性和所有权
* `Object.hasOwn()`
* `Object.prototype.propertyIsEnumerable()`
* `Object.create()`
* `Object.keys()`
* `Array.prototype.forEach()`


Object.getPrototypeOf() - JavaScript

Object.getPrototypeOf()
=======================

**`Object.getPrototypeOf()`** 静态方法返回指定对象的原型（即内部 `[[Prototype]]` 属性的值）。

尝试一下
----

语法
--

```js
Object.getPrototypeOf(obj)
```

### 参数

`obj`

要返回其原型的对象。

### 返回值

给定对象的原型，可能是 `null`。

示例
--

### 使用 getPrototypeOf

```js
const proto = {};
const obj = Object.create(proto);
Object.getPrototypeOf(obj) === proto; // true
```

### 非对象强制类型转换

在 ES5 中，如果 `obj` 参数不是对象，则会抛出 `TypeError` 异常。在 ES2015 中，该参数将被强制转换为 `Object`。

```js
Object.getPrototypeOf("foo");
// TypeError: "foo" is not an object (ES5 code)
Object.getPrototypeOf("foo");
// String.prototype (ES2015 code)
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-object.getprototypeof |

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

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

参见
--

* `core-js` 中 `Object.getPrototypeOf` 的 polyfill
* `Object.prototype.isPrototypeOf()`
* `Object.setPrototypeOf()`
* `Object.prototype.__proto__`
* John Resig 关于 getPrototypeOf 的博文。
* `Reflect.getPrototypeOf()`


Object.prototype.__proto__ - JavaScript

Object.prototype.\_\_proto\_\_
==============================

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

**警告：** 由于现代 JavaScript 引擎优化属性访问的性质，修改一个对象的 `[[Prototype]]` 在每个浏览器和 JavaScript 引擎中都是非常缓慢的操作。此外，修改继承的效果是微妙和广泛的，不仅限于在 `obj.__proto__ = ...` 语句中花费的时间，而是可能扩展到***任何***有访问任何已经被修改过 `[[Prototype]]` 的对象的代码。你可以在这里阅读更多信息：JavaScript 引擎基础：优化原型。

**备注：** 使用 `__proto__` 是有争议且不被鼓励的。它的存在和确切行为仅作为遗留特性被标准化，以确保 Web 兼容性，但它存在一些安全问题和隐患。为了更好的支持，请优先使用 `Object.getPrototypeOf()`/`Reflect.getPrototypeOf()` 和 `Object.setPrototypeOf()`/`Reflect.setPrototypeOf()`。

`Object` 实例的 **`__proto__`** 访问器属性暴露了此对象的 `[[Prototype]]`（一个对象或 `null`）。

`__proto__` 属性还可以在对象字面量定义中使用，作为创建对象时设置对象 `[[Prototype]]` 的一种替代方法，而不是使用 `Object.create()`。请参见：对象初始化/字面量语法。该语法已经标准化，并且在实现中得到了优化，与 `Object.prototype.__proto__` 相当不同。

语法
--

```js
obj.__proto__
```

### 返回值

如果作为 getter 使用，返回对象的 `[[Prototype]]`。

### 异常

`TypeError`

如果尝试设置一个不可扩展对象或不可修改原型的特异对象的原型，例如 `Object.prototype` 或 `window`，则会抛出错误。

描述
--

`__proto__` 的 getter 函数暴露了一个对象内部的 `[[Prototype]]` 的值。对于使用对象字面量创建的对象，该值是 `Object.prototype`。对于使用数组字面量创建的对象，该值是 `Array.prototype`。对于函数，该值是 `Function.prototype`。你可以在继承与原型链中了解有关原型链的更多信息。

`__proto__` 的 setter 允许修改一个对象的 `[[Prototype]]`。提供的值必须是一个对象或 `null`。提供任何其他值都不会产生任何作用。

与 `Object.getPrototypeOf()` 和 `Object.setPrototypeOf()` 不同，它们始终作为 `Object` 的静态属性存在并始终反映 `[[Prototype]]` 内部属性。`__proto__` 属性并不总是作为所有对象的属性存在，因此不能可靠地反映 `[[Prototype]]`。

`__proto__` 属性是 `Object.prototype` 上一个简单的访问器属性，由 getter 和 setter 函数组成。如果访问 `__proto__` 属性时最终查询到 `Object.prototype`，则会找到该属性，但如果没有查询 `Object.prototype`，则不会找到该属性。如果在查询 `Object.prototype` 之前就找到了其他的 `__proto__` 属性，则会覆盖 `Object.prototype` 上的 `__proto__` 属性。

`null` 原型对象不从 `Object.prototype` 继承任何属性，包括 `__proto__` 访问器属性。因此，如果你尝试在这样的对象上读取 `__proto__`，则无论对象的实际 `[[Prototype]]` 是什么，该值都将始终为 `undefined`，并且对 `__proto__` 的任何赋值都将创建一个名为 `__proto__` 的新属性，而不是设置对象的原型。此外，`__proto__` 可以通过 `Object.defineProperty()` 作为任何对象实例的自有属性重新定义，而不触发 setter。在这种情况下，`__proto__` 将不再是 `[[Prototype]]` 的访问器。因此，为设置和获取对象的 `[[Prototype]]`，请始终使用 `Object.getPrototypeOf()` 和 `Object.setPrototypeOf()`。

示例
--

### 使用 \_\_proto\_\_

```js
function Circle() {}
const shape = {};
const circle = new Circle();

// 设置该对象的原型
// 已弃用。这里只是举个例子，请不要在生产环境中这样做。
shape.__proto__ = circle;

// 判断该对象的原型链引用是否属于 circle
console.log(shape.__proto__ === circle); // true
```

```js
const ShapeA = function () {};
const ShapeB = {
  a() {
    console.log("aaa");
  },
};

ShapeA.prototype.__proto__ = ShapeB;
console.log(ShapeA.prototype.__proto__); // { a: [Function: a] }

const shapeA = new ShapeA();
shapeA.a(); // aaa
console.log(ShapeA.prototype === shapeA.__proto__); // true
```

```js
const ShapeC = function () {};
const ShapeD = {
  a() {
    console.log("a");
  },
};

const shapeC = new ShapeC();
shapeC.__proto__ = ShapeD;
shapeC.a(); // a
console.log(ShapeC.prototype === shapeC.__proto__); // false
```

```js
function Test() {}
Test.prototype.myName = function () {
  console.log("myName");
};

const test = new Test();
console.log(test.__proto__ === Test.prototype); // true
test.myName(); // myName

const obj = {};
obj.__proto__ = Test.prototype;
obj.myName(); // myName
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-object.prototype.\_\_proto\_\_ |

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

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

参见
--

* `Object.prototype.isPrototypeOf()`
* `Object.getPrototypeOf()`
* `Object.setPrototypeOf()`


Object.prototype.__lookupGetter__() - JavaScript

Object.prototype.\_\_lookupGetter\_\_()
=======================================

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

**备注：** 该特性已被弃用，建议使用 `Object.getOwnPropertyDescriptor()` API。该方法的行为只针对 Web 兼容性进行了规定，在任何平台上都不需要实现该方法。它可能无法在所有地方正常工作。

**`__lookupGetter__()`** 方法返回绑定到指定属性的 getter 函数。

语法
--

```js
\_\_lookupGetter\_\_(prop)
```

### 参数

`prop`

包含应返回其 getter 的属性名称的字符串。

### 返回值

绑定到指定属性的 getter 函数。如果找不到这样的属性或者该属性是数据属性，则返回 `undefined`。

描述
--

所有继承自 `Object.prototype` 的对象（也就是除了 `null` 原型对象的对象）都会继承 `__lookupGetter__()` 方法。如果为对象的属性定义了一个 getter，则无法通过该属性引用 getter 函数，因为该属性引用该函数的返回值。`__lookupGetter__()` 可用于获取对 getter 函数的引用。

`__lookupGetter__()` 沿着原型链向上查找指定的属性。如果原型链上的任何对象具有指定的自有属性，则返回该属性的属性描述符的 `get` 属性。如果该属性是数据属性，则返回 `undefined`。如果在整个原型链上都找不到该属性，也同样返回 `undefined`。

`__lookupGetter__()` 在规范中被定义为“可选规范”，这意味着不需要任何实现来实现它。然而，所有主要的浏览器都实现了它，并且由于它的持续使用，它不太可能被删除。如果一个浏览器实现了 `__lookupGetter__()`，那么它还需要实现 `__lookupSetter__()`、`__defineGetter__()` 和 `__defineSetter__()` 方法。

示例
--

### 使用 \_\_lookupGetter\_\_()

```js
const obj = {
  get foo() {
    return Math.random() > 0.5 ? "foo" : "bar";
  },
};

obj.\_\_lookupGetter\_\_("foo");
// [Function: get foo]
```

### 以标准方式查找属性的 getter

你应该使用 `Object.getOwnPropertyDescriptor()` API 来查找属性的 getter。与 `__lookupGetter__()` 相比，该方法允许查找 Symbol 属性。`Object.getOwnPropertyDescriptor()` 方法还适用于 `null` 原型对象，它们不继承自 `Object.prototype`，因此没有 `__lookupGetter__()` 方法。如果 `__lookupGetter__()` 沿着原型链向上查找属性的行为很重要，则可以使用 `Object.getPrototypeOf()` 自行实现它。

```js
const obj = {
  get foo() {
    return Math.random() > 0.5 ? "foo" : "bar";
  },
};

Object.getOwnPropertyDescriptor(obj, "foo").get;
// [Function: get foo]
```

```js
const obj2 = {
  \_\_proto\_\_: {
    get foo() {
      return Math.random() > 0.5 ? "foo" : "bar";
    },
  },
};

function findGetter(obj, prop) {
  while (obj) {
    const desc = Object.getOwnPropertyDescriptor(obj, "foo");
    if (desc) {
      return desc.get;
    }
    obj = Object.getPrototypeOf(obj);
  }
}

console.log(findGetter(obj2, "foo")); // [Function: get foo]
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-object.prototype.\_\_lookupGetter\_\_ |

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

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

参见
--

* `core-js` 中 `Object.prototype.__lookupGetter__` 的 polyfill
* `Object.prototype.__lookupSetter__()`
* `get` 语法
* `Object.getOwnPropertyDescriptor()` 和 `Object.getPrototypeOf()`
* `Object.prototype.__defineGetter__()`
* `Object.prototype.__defineSetter__()`
* JS 指南：定义 Getter 和 Setter


Object.entries() - JavaScript

Object.entries()
================

**`Object.entries()`** 静态方法返回一个数组，包含给定对象自有的可枚举字符串键属性的键值对。

尝试一下
----

语法
--

```js
Object.entries(obj)
```

### 参数

`obj`

一个对象。

### 返回值

一个由给定对象自有的可枚举字符串键属性的键值对组成的数组。每个键值对都是一个包含两个元素的数组：第一个元素是属性的键（始终是字符串），第二个元素是属性值。

描述
--

`Object.entries()` 返回一个数组，其元素是直接在 `object` 上找到相应的可枚举字符串键属性的键值对数组。这与使用 `for...in` 循环迭代相同，只是使用 `for...in` 循环也枚举原型链中的属性。`Object.entries()` 返回的数组顺序和 `for...in` 循环提供的顺序相同。

如果只需要属性的键，请使用 `Object.keys()`。如果只需要属性的值，请使用 `Object.values()`。

示例
--

### 使用 Object.entries()

```js
const obj = { foo: "bar", baz: 42 };
console.log(Object.entries(obj)); // [ ['foo', 'bar'], ['baz', 42] ]

// 类数组对象
const obj = { 0: "a", 1: "b", 2: "c" };
console.log(Object.entries(obj)); // [ ['0', 'a'], ['1', 'b'], ['2', 'c'] ]

// 具有随机键排序的类数组对象
const anObj = { 100: "a", 2: "b", 7: "c" };
console.log(Object.entries(anObj)); // [ ['2', 'b'], ['7', 'c'], ['100', 'a'] ]

// getFoo 是一个不可枚举的属性
const myObj = Object.create(
  {},
  {
    getFoo: {
      value() {
        return this.foo;
      },
    },
  },
);
myObj.foo = "bar";
console.log(Object.entries(myObj)); // [ ['foo', 'bar'] ]
```

### 在基本类型中使用 Object.entries()

非对象参数会强制转换成对象。`undefined` 和 `null` 不能被强制转换为对象，会立即抛出 `TypeError`。只有字符串可以有自己的可枚举属性，所有其他基本类型均返回一个空数组。

```js
// 字符串具有索引作为可枚举的自有属性
console.log(Object.entries("foo")); // [ ['0', 'f'], ['1', 'o'], ['2', 'o'] ]

// 其他基本类型（除了 undefined 和 null）没有自有属性
console.log(Object.entries(100)); // []
```

### 将 Object 转换成 Map

`Map()` 构造函数接受一个 `entries` 可迭代对象。使用 `Object.entries`，你可以很容易地将 `Object` 转换成 `Map`：

```js
const obj = { foo: "bar", baz: 42 };
const map = new Map(Object.entries(obj));
console.log(map); // Map(2) {"foo" => "bar", "baz" => 42}
```

### 遍历对象

使用数组解构语法，你可以很容易地遍历对象。

```js
// 使用 for...of 循环
const obj = { a: 5, b: 7, c: 9 };
for (const [key, value] of Object.entries(obj)) {
  console.log(`${key} ${value}`); // "a 5", "b 7", "c 9"
}

// 使用数组方法
Object.entries(obj).forEach(([key, value]) => {
  console.log(`${key} ${value}`); // "a 5", "b 7", "c 9"
});
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-object.entries |

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

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

参见
--

* `core-js` 中 `Object.entries` 的 Polyfill
* 属性的可枚举性和所有权
* `Object.keys()`
* `Object.values()`
* `Object.prototype.propertyIsEnumerable()`
* `Object.create()`
* `Object.fromEntries()`
* `Object.getOwnPropertyNames()`
* `Map.prototype.entries()`


Object.create() - JavaScript

Object.create()
===============

**`Object.create()`** 静态方法以一个现有对象作为原型，创建一个新对象。

尝试一下
----

语法
--

```js
Object.create(proto)
Object.create(proto, propertiesObject)
```

### 参数

`proto`

新创建对象的原型对象。

`propertiesObject` 可选

如果该参数被指定且不为 `undefined`，则该传入对象可枚举的自有属性将为新创建的对象添加具有对应属性名称的属性描述符。这些属性对应于 `Object.defineProperties()` 的第二个参数。

### 返回值

根据指定的原型对象和属性创建的新对象。

### 异常

`TypeError`

如果 `proto` 既不是 `null`，也不是 `Object`，则抛出此错误。

示例
--

### 用 Object.create() 实现类式继承

下面的例子演示了如何使用 `Object.create()` 来实现类式继承。这是一个所有版本 JavaScript 都支持的单继承。

```js
// Shape——父类
function Shape() {
  this.x = 0;
  this.y = 0;
}

// 父类方法
Shape.prototype.move = function (x, y) {
  this.x += x;
  this.y += y;
  console.info("Shape moved.");
};

// Rectangle——子类
function Rectangle() {
  Shape.call(this); // 调用父类构造函数。
}

// 子类继承父类
Rectangle.prototype = Object.create(Shape.prototype, {
  // 如果不将 Rectangle.prototype.constructor 设置为 Rectangle，
  // 它将采用 Shape（父类）的 prototype.constructor。
  // 为避免这种情况，我们将 prototype.constructor 设置为 Rectangle（子类）。
  constructor: {
    value: Rectangle,
    enumerable: false,
    writable: true,
    configurable: true,
  },
});

const rect = new Rectangle();

console.log("rect 是 Rectangle 类的实例吗？", rect instanceof Rectangle); // true
console.log("rect 是 Shape 类的实例吗？", rect instanceof Shape); // true
rect.move(1, 1); // 打印 'Shape moved.'
```

需要注意的是，使用 `create()` 也有一些要注意的地方，比如重新添加 `constructor` 属性以确保正确的语义。虽然 `Object.create()` 被认为比使用 `Object.setPrototypeOf()` 修改原型更具有性能优势，但如果没有创建实例并且属性访问还没有被优化，它们之间的差异实际上是可以忽略不计的。在现代代码中，无论如何都应该优先使用类语法。

### 使用 Object.create() 的 propertyObject 参数

`Object.create()` 方法允许对对象创建过程进行精细的控制。实际上，字面量初始化对象语法是 `Object.create()` 的一种语法糖。使用 `Object.create()`，我们可以创建具有指定原型和某些属性的对象。请注意，第二个参数将键映射到*属性描述符*，这意味着你还可以控制每个属性的可枚举性、可配置性等，而这在字面量初始化对象语法中是做不到的。

```js
o = {};
// 等价于：
o = Object.create(Object.prototype);

o = Object.create(Object.prototype, {
  // foo 是一个常规数据属性
  foo: {
    writable: true,
    configurable: true,
    value: "hello",
  },
  // bar 是一个访问器属性
  bar: {
    configurable: false,
    get() {
      return 10;
    },
    set(value) {
      console.log("Setting `o.bar` to", value);
    },
  },
});

// 创建一个新对象，它的原型是一个新的空对象，并添加一个名为 'p'，值为 42 的属性。
o = Object.create({}, { p: { value: 42 } });
```

使用 `Object.create()`，我们可以创建一个原型为 `null` 的对象。在字面量初始化对象语法中，相当于使用 `__proto__` 键。

```js
o = Object.create(null);
// 等价于：
o = { \_\_proto\_\_: null };
```

默认情况下，属性是*不可写*、*可枚举*和*可配置*的。

```js
o.p = 24; // 在严格模式下会报错
o.p; // 42

o.q = 12;
for (const prop in o) {
  console.log(prop);
}
// 'q'

delete o.p;
// false；在严格模式下会报错
```

如果要指定与字面量对象中相同的属性，请显式指定 `writable`、`enumerable` 和 `configurable`。

```js
o2 = Object.create(
  {},
  {
    p: {
      value: 42,
      writable: true,
      enumerable: true,
      configurable: true,
    },
  },
);
// 这与以下语句不等价：
// o2 = Object.create({ p: 42 })
// 后者将创建一个原型为 { p: 42 } 的对象。
```

你可以使用 `Object.create()` 来模仿 `new` 运算符的行为。

```js
function Constructor() {}
o = new Constructor();
// 等价于：
o = Object.create(Constructor.prototype);
```

当然，如果 `Constructor` 函数中有实际的初始化代码，那么 `Object.create()` 方法就无法反映它。

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-object.create |

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

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

参见
--

* `core-js` 中 `Object.create` 的 polyfill
* `Object.defineProperty()`
* `Object.defineProperties()`
* `Object.prototype.isPrototypeOf()`
* `Reflect.construct()`
* John Resig 发布的关于 getPrototypeOf() 的博客


Object() 构造函数 - JavaScript

Object() 构造函数
=============

**`Object()`** 构造函数将输入转换为一个对象。它的行为取决于输入的类型。

语法
--

```js
new Object(value)
Object(value)
```

**备注：** `Object()` 可以在带有或者不带有 `new` 的情况下调用，但有时会产生不同的效果。参见返回值。

### 参数

`value`

任意值。

### 返回值

当调用或者构造 `Object()` 构造函数本身时，其返回值是一个对象：

* 如果该值是 `null` 或者 `undefined`，它会生成并返回一个空对象。
* 如果该值已经是一个对象，则返回该值。
* 否则，它将返回与给定值对应的类型的对象。例如，传递 `BigInt` 基本类型会返回一个 `BigInt` 封装对象。

当通过继承 `Object` 的类的构造函数中的 `super()` 隐式调用 `Object()` 时，它以 `new.target.prototype` 为原型初始化一个新对象。传递给 `super()` 的任意值都将被忽略——例如，即使你传递一个数字，构造函数中的 `this` 值也不会变成 `Number` 实例。

示例
--

### 创建一个新对象

```js
const o = new Object();
o.foo = 42;

console.log(o);
// { foo: 42 }
```

### 使用 undefined 和 null 类型调用 Object

以下示例在 `o` 中存储一个空 `Object` 对象：

```js
const o = new Object();
```

```js
const o = new Object(undefined);
```

```js
const o = new Object(null);
```

### 获取 BigInt 和 Symbol 的封装对象

当用 `new` 调用 `BigInt()` 和 `Symbol()` 构造函数时会抛出一个错误，以阻止创建封装对象而不是基本类型值的常见错误。为这些类型创建封装对象的唯一方法是使用它们调用 `Object()`：

```js
const numberObj = new Number(1);
console.log(typeof numberObj); // "object"

const bigintObj = Object(1n);
console.log(typeof bigintObj); // "object"

const symbolObj = Object(Symbol("foo"));
console.log(typeof symbolObj); // "object"
```

规范
--

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

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

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

参见
--

* 对象初始化


Object.getOwnPropertyDescriptors() - JavaScript

Object.getOwnPropertyDescriptors()
==================================

**`Object.getOwnPropertyDescriptors()`** 静态方法返回给定对象的所有自有属性描述符。

尝试一下
----

语法
--

```js
Object.getOwnPropertyDescriptors(obj)
```

### 参数

`obj`

要获取其所有自有属性描述符的对象。

### 返回值

一个包含给定对象的所有自有属性描述符的对象。如果没有属性，则可能是一个空对象。

描述
--

该方法允许查看对象的所有自有属性的精确描述。在 JavaScript 中，一个*属性*由一个字符串值的名称或一个 `Symbol` 和一个属性描述符组成。关于属性描述符类型及其特性的更多信息可以在 `Object.defineProperty()` 中找到。

一个*属性描述符*是一个记录，具有以下一些特性：

`value`

与属性关联的值（仅限数据描述符）。

`writable`

当且仅当与属性关联的值可以更改时，为 `true`（仅限数据描述符）。

`get`

作为属性 getter 的函数，如果没有 getter 则为 `undefined`（仅限访问器描述符）。

`set`

作为属性 setter 的函数，如果没有 setter 则为 `undefined`（仅限访问器描述符）。

`configurable`

当且仅当此属性描述符的类型可以更改且该属性可以从相应对象中删除时，为 `true`。

`enumerable`

当且仅当此属性在相应对象的属性枚举中出现时，为 `true`。

示例
--

### 浅拷贝一个对象

而 `Object.assign()` 方法仅将可枚举的自有属性从源对象复制到目标对象，你可以使用此方法和 `Object.create()` 方法对两个未知对象进行浅拷贝：

```js
Object.create(
  Object.getPrototypeOf(obj),
  Object.getOwnPropertyDescriptors(obj),
);
```

### 创建子类

创建子类的典型方法是定义子类，将其原型设置为超类的实例，然后在该实例上定义属性。这么写很不优雅，特别是对于 getter 和 setter 而言。相反，你可以使用此代码设置原型：

```js
function superclass() {}
superclass.prototype = {
  // 在这里定义超类的构造方法、方法和属性
};
function subclass() {}
subclass.prototype = Object.create(superclass.prototype, {
  // 在这里定义子类的构造方法、方法和属性
});
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-object.getownpropertydescriptors |

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

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

参见
--

* `core-js` 中 `Object.getOwnPropertyDescriptors` 的 polyfill
* `Object.getOwnPropertyDescriptor()`
* `Object.defineProperty()`
* Polyfill


Object.isExtensible() - JavaScript

Object.isExtensible()
=====================

**`Object.isExtensible()`** 静态方法判断一个对象是否是可扩展的（是否可以在它上面添加新的属性）。

尝试一下
----

语法
--

```js
Object.isExtensible(obj)
```

### 参数

`obj`

要检查的对象。

### 返回值

指示给定对象是否可扩展的一个布尔值。

描述
--

默认情况下，对象是可扩展的：可以向它们添加新属性，并且它们的 `[[Prototype]]` 可以被重新赋值。可以使用 `Object.preventExtensions()`、`Object.seal()`、`Object.freeze()` 或 `Reflect.preventExtensions()` 中的任一方法将对象标记为不可扩展。

示例
--

### 使用 Object.isExtensible

```js
// 新对象是可拓展的。
const empty = {};
Object.isExtensible(empty); // true

// 它们可以变为不可拓展的
Object.preventExtensions(empty);
Object.isExtensible(empty); // false

// 根据定义，密封对象是不可拓展的。
const sealed = Object.seal({});
Object.isExtensible(sealed); // false

// 根据定义，冻结对象同样也是不可拓展的。
const frozen = Object.freeze({});
Object.isExtensible(frozen); // false
```

### 非对象参数

在 ES5 中，如果参数不是一个对象（即基本类型），将抛出 `TypeError`。在 ES2015 中，如果传入的参数不是一个对象，那么它将返回 `false` 而不会报错，因为按照定义，原始类型是不可变的。

```js
Object.isExtensible(1);
// TypeError: 1 is not an object (ES5 code)

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

规范
--

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

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

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

参见
--

* `Object.preventExtensions()`
* `Object.seal()`
* `Object.isSealed()`
* `Object.freeze()`
* `Object.isFrozen()`
* `Reflect.isExtensible()`


Object.freeze() - JavaScript

Object.freeze()
===============

`Object.freeze()` 静态方法可以使一个对象被*冻结*。冻结对象可以防止扩展，并使现有的属性不可写入和不可配置。被冻结的对象不能再被更改：不能添加新的属性，不能移除现有的属性，不能更改它们的可枚举性、可配置性、可写性或值，对象的原型也不能被重新指定。`freeze()` 返回与传入的对象相同的对象。

冻结一个对象是 JavaScript 提供的最高完整性级别保护措施。

尝试一下
----

语法
--

```js
Object.freeze(obj)
```

### 参数

`obj`

要冻结的对象。

### 返回值

传递给函数的对象。

描述
--

冻结一个对象相当于阻止其扩展然后将所有现有属性的描述符的 `configurable` 特性更改为 `false`——对于数据属性，将同时把 `writable` 特性更改为 `false`。无法向被冻结的对象的属性中添加或删除任何内容。任何这样的尝试都将失败，可能是静默失败，也可能抛出一个 `TypeError` 异常（通常情况下，在严格模式中抛出）。

对于被冻结对象的数据属性，它们的值不能被更改，因为它们的 `writable` 和 `configurable` 特性被设置为 `false`。访问器属性（getter 和 setter）也相同——getter 返回的属性值仍然可以更改，setter 可以在设置属性时调用而不抛出错误。请注意，对象类型的值仍然可以修改，除非它们也被冻结。数组作为一种对象也可以被冻结；数组被冻结后，既不能更改它的元素，也不能向数组中添加或删除元素。

`freeze()` 返回传递给函数的同一对象。它*不会*创建一个被冻结的副本。

尝试冻结带有元素的 `TypedArray` 或 `DataView` 会导致 `TypeError`，因为它们是内存视图，可能会引起其他问题：

```js
Object.freeze(new Uint8Array(0)); // 没有元素
// Uint8Array []

Object.freeze(new Uint8Array(1)); // 有元素
// TypeError: Cannot freeze array buffer views with elements

Object.freeze(new DataView(new ArrayBuffer(32))); // 没有元素
// DataView {}

Object.freeze(new Float64Array(new ArrayBuffer(64), 63, 0)); // 没有元素
// Float64Array []

Object.freeze(new Float64Array(new ArrayBuffer(64), 32, 2)); // 有元素
// TypeError: Cannot freeze array buffer views with elements
```

请注意，由于标准中的三个属性（`buf.byteLength`、`buf.byteOffset` 和 `buf.buffer`）是只读的（就像 `ArrayBuffer` 或 `SharedArrayBuffer` 的属性一样），因此没有理由尝试冻结这些属性。

与 `Object.seal()` 不同，使用 `Object.freeze()` 冻结的对象中的现有属性是不可变的，数据属性不能被重新赋值。

示例
--

### 冻结对象

```js
const obj = {
  prop() {},
  foo: "bar",
};

// 冻结前：可以添加新属性，也可以更改或删除现有属性
obj.foo = "baz";
obj.lumpy = "woof";
delete obj.prop;

// 冻结。
const o = Object.freeze(obj);

// 返回值和我们传入的对象相同。
o === obj; // true

// 对象已冻结。
Object.isFrozen(obj); // === true

// 现在任何更改都会失败。
obj.foo = "quux"; // 静默但什么都没做
// 静默且没有添加属性
obj.quaxxor = "the friendly duck";

// 严格模式下，这样的尝试会抛出 TypeError
function fail() {
  "use strict";
  obj.foo = "sparky"; // 抛出 TypeError
  delete obj.foo; // 抛出 TypeError
  delete obj.quaxxor; // 返回 true，因为属性‘quaxxor’从未被添加过。
  obj.sparky = "arf"; // 抛出 TypeError
}

fail();

// 尝试通过 Object.defineProperty 更改；
// 下面的两个语句都会抛出 TypeError。
Object.defineProperty(obj, "ohai", { value: 17 });
Object.defineProperty(obj, "foo", { value: "eit" });

// 同样无法更改原型
// 下面的两个语句都会抛出 TypeError。
Object.setPrototypeOf(obj, { x: 20 });
obj.__proto__ = { x: 20 };
```

### 冻结数组

```js
const a = [0];
Object.freeze(a); // 数组现在开始无法被修改

a[0] = 1; // 静默失败

// 严格模式下，这样的尝试将抛出 TypeError
function fail() {
  "use strict";
  a[0] = 1;
}

fail();

// 尝试在数组末尾追加元素
a.push(2); // 抛出 TypeError
```

被冻结的对象是*不可变*的。但也不总是这样。以下示例显示被冻结的对象不是*常量*（浅冻结）。

```js
const obj1 = {
  internal: {},
};

Object.freeze(obj1);
obj1.internal.a = "aValue";

obj1.internal.a; // 'aValue'
```

对于一个常量对象，整个引用图（直接和间接引用其他对象）只能引用不可变的冻结对象。冻结的对象被认为是不可变的，因为整个对象中的整个对象*状态*（对其他对象的值和引用）是固定的。注意，字符串、数字和布尔值总是不可变的，而函数和数组是对象。

#### 什么是“浅冻结”？

调用 `Object.freeze(object)` 的结果仅适用于 `object` 本身的直接属性，并且*只会*在 `object` 上防止未来的属性添加、删除，或重新赋值的操作。如果这些属性的值本身是对象，这些对象不会被冻结，并且可能成为属性添加、删除，或重新赋值操作的目标。

```js
const employee = {
  name: "Mayank",
  designation: "Developer",
  address: {
    street: "Rohini",
    city: "Delhi",
  },
};

Object.freeze(employee);

employee.name = "Dummy"; // 在非严格模式下静默失败
employee.address.city = "Noida"; // 可以修改子对象的属性

console.log(employee.address.city); // "Noida"
```

为了使对象不可变，需要递归地冻结每个对象类型的属性（深冻结）。当且仅当你知道对象在引用图中不包含循环引用时，可以根据你的设计逐个案例地使用该模式，否则会导致无限循环。`deepFreeze()` 的一个增强功能是添加一个内部函数，该函数接收一个路径（例如一个数组）参数，这样可以在调用 `deepFreeze()` 递归地冻结对象时，避免冻结正在被处理的对象。但仍有可能会冻结不应该被冻结的对象，例如 `window`。

```js
function deepFreeze(object) {
  // 获取对象的属性名
  const propNames = Reflect.ownKeys(object);

  // 冻结自身前先冻结属性
  for (const name of propNames) {
    const value = object[name];

    if ((value && typeof value === "object") || typeof value === "function") {
      deepFreeze(value);
    }
  }

  return Object.freeze(object);
}

const obj2 = {
  internal: {
    a: null,
  },
};

deepFreeze(obj2);

obj2.internal.a = "anotherValue"; // 非严格模式下会静默失败
obj2.internal.a; // null
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-object.freeze |

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

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

参见
--

* `Object.isFrozen()`
* `Object.preventExtensions()`
* `Object.isExtensible()`
* `Object.seal()`
* `Object.isSealed()`


RegExp.prototype.multiline - JavaScript

RegExp.prototype.multiline
==========================

概述
--

**`multiline`** 属性表明正则表达式是否使用了 "`m`" 标志。`multiline` 是正则表达式实例的一个只读属性。

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

描述
--

`multiline` 是一个布尔对象，如果使用了 "`m`" 标志，则返回 `true`；否则，返回 `false`。"`m`" 标志意味着一个多行输入字符串被看作多行。例如，使用 "`m`"，"`^`" 和 "`$`" 将会从只匹配正则字符串的开头或结尾，变为匹配字符串中任一行的开头或结尾。

你无法直接更改此属性。

示例
--

### 示例：使用 `multiline`

```js
var regex = new RegExp("foo", "m");

console.log(regex.multiline); // true
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-get-regexp.prototype.multiline |

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

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

参见
--

* `RegExp.prototype.global`
* `RegExp.prototype.lastIndex`
* `RegExp.prototype.ignoreCase`
* `RegExp.prototype.source`
* `RegExp.prototype.sticky`


RegExp.prototype.sticky - JavaScript

RegExp.prototype.sticky
=======================

**`sticky`** 属性反映了搜索是否具有粘性（仅从正则表达式的 `lastIndex` 属性表示的索引处搜索）。`sticky` 是正则表达式对象的只读属性。

尝试一下
----

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

描述
--

`sticky` 的值是 `Boolean` ，并在 `y` 标志使用时为真; 否则为假。`y` 标志指示，仅从正则表达式的 `lastIndex` 属性表示的索引处为目标字符串匹配（并且不会尝试从后续索引匹配）。如果一个表达式同时指定了 `sticky` 和 `global`，其将会忽略 `global` 标志。

你不能直接更改这个属性，它是只读的。

示例
--

### 使用带 sticky 标志的正则表达式

```js
var str = "#foo#";
var regex = /foo/y;

regex.lastIndex = 1;
regex.test(str); // true（译注：此例仅当 lastIndex = 1 时匹配成功，这就是 sticky 的作用）
regex.lastIndex = 5;
regex.test(str); // false（lastIndex 被 sticky 标志考虑到，从而导致匹配失败）
regex.lastIndex; // 0（匹配失败后重置）
```

### 锚定的 sticky 标志

火狐的 SpiderMonkey 引擎的几个版本有一个 bug，处理 `^` 断言和 sticky 标志时，会允许使用了 sticky 标志的表达式从 `^` 断言开始匹配，这是不对的。这个 bug 是在 Firefox 3.6 之后的某个版本引入的（which had the sticky flag but not the bug）并于 2015 年修复。可能正因为这个 bug，ES2015 规范 特别指出：

>
> 当使用带有 `y` 标识的匹配模式时，^ 断言总是会匹配输入的开始位置或者（如果是多行模式）每一行的开始位置。
>
>
>

正确行为的示例：

```js
var regex = /^foo/y;
regex.lastIndex = 2;
regex.test("..foo"); // false - 索引 2 不是字符串的开始

var regex2 = /^foo/my;
regex2.lastIndex = 2;
regex2.test("..foo"); // false - 索引 2 不是字符串或行的开始
regex2.lastIndex = 2;
regex2.test(".\nfoo"); // true - 索引 2 是行的开始
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-get-regexp.prototype.sticky |

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

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

参见
--

* `RegExp.lastIndex`
* `RegExp.prototype.global`
* `RegExp.prototype.ignoreCase`
* `RegExp.prototype.multiline`
* `RegExp.prototype.source`


RegExp.prototype.source - JavaScript

RegExp.prototype.source
=======================

概述
--

**`source`** 属性返回一个值为当前正则表达式对象的模式文本的字符串，该字符串不会包含正则字面量两边的斜杠以及任何的标志字符。

示例
--

```js
var regex = /fooBar/gi;

console.log(regex.source); // "fooBar"，不包含 /.../ 和 "ig"。
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-get-regexp.prototype.source |

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

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

参见
--

* `RegExp.prototype.flags`


RegExp.prototype.flags - JavaScript

RegExp.prototype.flags
======================

`RegExp` 实例的 **`flags`** 访问器属性返回当前正则表达式的标志。

尝试一下
----

描述
--

`RegExp.prototype.flags` 具有一个字符串值。`flags` 属性中的标志按字母顺序排列（从左到右，例如 `"dgimsuvy"`）。它实际上调用了其他标志访问器（如 `hasIndices`、`global` 等）并逐个连接这些结果。

所有内置函数都是读取 `flags` 属性，而不是读取单独的标志访问器。

`flags` 的 set 访问器是 `undefined`。因此你不能直接修改该属性。

示例
--

### 使用 flags

```js
/foo/gi.flags; // "gi"
/bar/muy.flags; // "muy"
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-get-regexp.prototype.flags |

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

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

参见
--

* `core-js` 中 `RegExp.prototype.flags` 的 polyfill
* `RegExp.prototype.source`


RegExp.lastMatch ($&) - JavaScript

RegExp.lastMatch ($&)
=====================

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

**lastMatch** 非标准属性是正则表达式的静态和只读属性，含有最后匹配到的字符串。`RegExp.$&` 是这个属性的别名。

语法
--
```

RegExp.lastMatch
RegExp['$&']
```

描述
--

`lastMatch` 属性是静态的，不是正则表达式独立对象的属性。反之，你应始终将其使用为 `RegExp.lastMatch` 或者 `RegExp['$&']`。

`lastMatch` 属性的值是只读的，并且会在匹配成功时修改。

你不能使用属性访问器 (`RegExp.$&`) 来使用简写的别名，因为解析器在这里会将 "&" 看做表达式，并抛出 `SyntaxError` 。使用 方括号符号来访问属性。

示例
--

### 使用 `lastMatch` 和 `$&`

```js
var re = /hi/g;
re.test("hi there!");
RegExp.lastMatch; // "hi"
RegExp["$&"]; // "hi"
```

规范
--

非标准。并不是任何现行规范的一部分。

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

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

另见
--

* 非标准
 `RegExp.input ($_)`
* 非标准
 `RegExp.lastParen ($+)`
* 非标准
 `RegExp.leftContext ($`)`
* 非标准
 `RegExp.rightContext ($')`
* 非标准
 `RegExp.$1-$9`


RegExp.prototype[@@search]() - JavaScript

RegExp.prototype[@@search]()
============================

**`[@@search]()`** 方法执行了一个在给定字符串中的一个搜索以取得匹配正则模式的项。

语法
--
```

regexp[Symbol.search](str)
```

### 参数

`str`

搜索的目标 `String`。

### 返回值

整数

如果成功的话，`[@@search]()` 返回该正则模式的第一个匹配项的在字符串中的位置索引。否则将返回 -1。

描述
--

这个方法在 `String.prototype.search()` 的内部调用。例如，下面的两个方法返回相同结果。

```js
"abc".search(/a/);

/a/[Symbol.search]("abc");
```

这个方法为自定义 `RegExp` 子类中的匹配行为而存在。

示例
--

### 直接调用

这个方法的使用方式和 `String.prototype.search()` 相同，不同之处是 `this` 和参数顺序。

```js
var re = /-/g;
var str = "2016-01-02";
var result = re[Symbol.search](str);
console.log(result); // 4
```

### 在子类中使用`@@search`

{jsxref("RegExp")}} 的子类可以覆写 `[@@search]()`方法来修改默认行为。

```js
class MyRegExp extends RegExp {
  constructor(str) {
    super(str);
    this.pattern = str;
  }
  [Symbol.search](str) {
    return str.indexOf(this.pattern);
  }
}

var re = new MyRegExp("a+b");
var str = "ab a+b";
var result = str.search(re); // String.prototype.search calls re[@@search].
console.log(result); // 3
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-regexp.prototype-@@search |

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

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

另见
--

* `String.prototype.search()`
* `RegExp.prototype[@@match]()`
* `RegExp.prototype[@@replace]()`
* `RegExp.prototype[@@split]()`
* `RegExp.prototype.exec()`
* `RegExp.prototype.test()`


RegExp.prototype.toString() - JavaScript

RegExp.prototype.toString()
===========================

概述
--

**`toString()`** 返回一个表示该正则表达式的字符串。

语法
--
```

regexObj.toString()
```

### 参数

无

描述
--

`RegExp` 对象覆盖了 `Object` 对象的 `toString()` 方法，并没有继承 `Object.prototype.toString()`。对于 `RegExp` 对象，`toString` 方法返回一个该正则表达式的字符串形式。

示例
--

### 示例：使用 `toString`

下例输出 `RegExp` 对象的字符串值：
```

myExp = new RegExp("a+b+c");
alert(myExp.toString());       // 显示 "/a+b+c/"

foo = new RegExp("bar", "g");
alert(foo.toString());         // 显示 "/bar/g"
```

规范
--

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

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

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

参见
--

* `Object.prototype.toString()`


RegExp.prototype[@@match]() - JavaScript

RegExp.prototype[@@match]()
===========================

对*正则表达式*匹配*字符串*时，\*\*`[@@match]()`\*\*方法用于获取匹配结果。

尝试一下
----

语法
--
```

regexp[Symbol.match](str)
```

### 参数

`str`

match 的目标参数是`String`

### 返回值

match 方法会返回一个数组，它包括整个匹配结果，和通过捕获组匹配到的结果，如果没有匹配到则返回 null

描述
--

这个方法在 `String.prototype.match()` 的内部调用。例如，下面的两个方法返回相同结果。

```js
"abc".match(/a/);

/a/[Symbol.match]("abc");
```

这个方法为自定义 `RegExp` 子类中的匹配行为而存在。

示例
--

### 直接调用

这个方法的使用方式和 `String.prototype.match()` 相同，不同之处是 `this` 和参数顺序。

```js
var re = /[0-9]+/g;
var str = "2016-01-02";
var result = re[Symbol.match](str);
console.log(result); // ["2016", "01", "02"]
```

### 在子类中使用`@@match`

`RegExp` 的子类可以覆写 `[@@match]()`方法来修改默认行为。

```js
class MyRegExp extends RegExp {
  [Symbol.match](str) {
    var result = RegExp.prototype[Symbol.match].call(this, str);
    if (!result) return null;
    return {
      group(n) {
        return result[n];
      },
    };
  }
}

var re = new MyRegExp("([0-9]+)-([0-9]+)-([0-9]+)");
var str = "2016-01-02";
var result = str.match(re); // String.prototype.match calls re[@@match].
console.log(result.group(1)); // 2016
console.log(result.group(2)); // 01
console.log(result.group(3)); // 02
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-regexp.prototype-@@match |

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

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

另见
--

* `String.prototype.match()`
* `RegExp.prototype[@@replace]()`
* `RegExp.prototype[@@search]()`
* `RegExp.prototype[@@split]()`
* `RegExp.prototype.exec()`
* `RegExp.prototype.test()`


RegExp.prototype.global - JavaScript

RegExp.prototype.global
=======================

概述
--

**`global`** 属性表明正则表达式是否使用了 "`g`" 标志。`global` 是一个正则表达式实例的只读属性。

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

描述
--

`global` 的值是布尔对象，如果使用了 "`g`" 标志，则返回 `true`；否则返回 `false`。 "`g`" 标志意味着正则表达式应该测试字符串中所有可能的匹配。

你无法直接更改此属性。

示例
--

### 示例：使用 `global`

```js
var regex = new RegExp("foo", "g");

console.log(regex.global); // true
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-get-regexp.prototype.global |

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

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

参见
--

* `RegExp.prototype.lastIndex`
* `RegExp.prototype.dotAll`
* `RegExp.prototype.hasIndices`
* `RegExp.prototype.ignoreCase`
* `RegExp.prototype.multiline`
* `RegExp.prototype.source`
* `RegExp.prototype.sticky`
* `RegExp.prototype.unicode`


RegExp.$1-$9 - JavaScript

RegExp.$1-$9
============

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

非标准 **$1, $2, $3, $4, $5, $6, $7, $8, $9** 属性是包含括号子串匹配的正则表达式的静态和只读属性。

语法
--
```

RegExp.$1
RegExp.$2
RegExp.$3
RegExp.$4
RegExp.$5
RegExp.$6
RegExp.$7
RegExp.$8
RegExp.$9
```

描述
--

$1, ..., $9 属性是静态的，他不是独立的正则表达式属性。所以，我们总是像这样子使用他们`RegExp.$1`, ..., `RegExp.$9`.

属性的值是只读的而且只有在正确匹配的情况下才会改变。

括号匹配项是无限的，但是 RegExp 对象能捕获的只有九个。你可以通过返回一个数组索引来取得所有的括号匹配项。

这些属性可以在`String.replace` 方法中替换字符串。在这种情况下，不用在前面加上 RegExp。下面的例子将详细说明。当正则表达式中不包含括号，脚本中的 `$n`'s 就是字面上的意思 (当 n 是正整数).

示例
--

### `$n` 在 `String.replace 中的应用`

以下脚本用 `replace()` 方法去匹配一个 first last 格式的 name`String` 实例 输出 last first 格式。在替换文本里，脚本用 `$1` 和 `$2` 表示正则表达式中的括号匹配项的结果。

```js
var re = /(\w+)\s(\w+)/;
var str = "John Smith";
str.replace(re, "$2, $1"); // "Smith, John"
RegExp.$1; // "John"
RegExp.$2; // "Smith"
```

技术指标
----

非标准。不属于当前的任何规范。

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

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

参见
--

* 非标准
 `RegExp.input ($_)`
* 非标准
 `RegExp.lastMatch ($&)`
* 非标准
 `RegExp.lastParen ($+)`
* 非标准
 `RegExp.leftContext ($`)`
* 非标准
 `RegExp.rightContext ($')`


RegExp.prototype.unicode - JavaScript

RegExp.prototype.unicode
========================

**`unicode`** 属性表明正则表达式带有"`u`" 标志。 `unicode` 是正则表达式独立实例的只读属性。

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

描述
--

`unicode` 的值是 `Boolean`，并且如果使用了 "`u`" 标志则为 `true`；否则为 `false`。"`u`" 标志开启了多种 Unicode 相关的特性。使用 "u" 标志，任何 Unicode 代码点的转义都会被解释。

你不能直接修改这个属性，它是只读的。

示例
--

### 使用 `unicode` 属性

```js
var regex = new RegExp("\u{61}", "u");

console.log(regex.unicode); // true
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-get-regexp.prototype.unicode |

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

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

另见
--

* `RegExp.lastIndex`
* `RegExp.prototype.global`
* `RegExp.prototype.ignoreCase`
* `RegExp.prototype.multiline`
* `RegExp.prototype.source`
* `RegExp.prototype.sticky`


RegExp.lastIndex - JavaScript

RegExp.lastIndex
================

概述
--

**`lastIndex`** 是正则表达式的一个可读可写的整型属性，用来指定下一次匹配的起始索引。

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

语法
--

```js
lastIndex = regExpObj.lastIndex;
```

描述
--

只有正则表达式使用了表示全局检索的 "`g`" 或者粘性检索的 "`y`" 标志时，该属性才会起作用。此时应用下面的规则：

* 如果 `lastIndex` 大于字符串的长度，则 `regexp.test` 和 `regexp.exec` 将会匹配失败，然后 `lastIndex` 被设置为 0。
* 如果 `lastIndex` 等于或小于字符串的长度，则该正则表达式匹配从 `lastIndex` 位置开始的字符串。
    + 如果 `regexp.test` 和 `regexp.exec` 匹配成功，`lastIndex` 会被设置为紧随最近一次成功匹配的下一个位置。
    + 如果 `regexp.test` 和 `regexp.exec` 匹配失败，`lastIndex` 会被设置为 0
示例
--

考虑下面的语句：

```js
var re = /(hi)?/g;
```

匹配空字符串

```js
console.log(re.exec("hi"));
console.log(re.lastIndex);
```

返回 `["hi", "hi"]` ，`lastIndex` 等于 2。

```js
console.log(re.exec("hi"));
console.log(re.lastIndex);
```

返回 `["", undefined]`，即一个数组，其第 0 个元素为匹配的字符串。此种情况下为空字符串，是因为 `lastIndex` 为 2（且一直是 2），"`hi`" 长度为 2。

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-properties-of-regexp-instances |

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

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

参见
--

* `RegExp.prototype.ignoreCase`
* `RegExp.prototype.global`
* `RegExp.prototype.multiline`
* `RegExp.prototype.source`
* `RegExp.prototype.sticky`


RegExp.prototype.dotAll - JavaScript

RegExp.prototype.dotAll
=======================

**`dotAll`** 属性表明是否在正则表达式中一起使用"`s`"修饰符（引入/s 修饰符，使得。可以匹配任意单个字符）。`dotAll` 是一个只读的属性，属于单个正则表达式实例。

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

描述
--

如果使用了"`s`"修饰符，`dotAll` 的值将返回`Boolean`类型的`true`，否则将返回`false`。"`s`"修饰符表示，特殊字符"`.`"应另外匹配字符串中的下述行终结符（line terminator characters），否则将会失配：

* U+000A 换行符（"`\n`"）
* U+000D 回车符（"`\r`"）
* U+2028 行分隔符（line separator）
* U+2029 段分隔符（paragraph separator）

这实际上意味着"`.`"将会匹配任意的单个 Unicode Basic Multilingual Plane (BMP) 字符。若要使其与 astral 字符（大于 \uFFFF 的 Unicode 字符）匹配，你应当使用"`u`"（Unicode）修饰符。一起使用这两个修饰符，"`.`"将无一例外地匹配任意 Unicode 字符。

无法直接修改此属性。

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

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

参见
--

* `RegExp.lastIndex`
* `RegExp.prototype.global`
* `RegExp.prototype.ignoreCase`
* `RegExp.prototype.multiline`
* `RegExp.prototype.source`
* `RegExp.prototype.sticky`
* `RegExp.prototype.unicode`


RegExp.rightContext ($') - JavaScript

RegExp.rightContext ($')
========================

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

**rightContext** 非标准属性是正则表达式的静态和只读属性，含有最新匹配的右侧子串。 `RegExp.$'` 是这个属性的别名。

语法
--
```

RegExp.rightContext
RegExp["$'"]
```

描述
--

`rightContext` 属性是静态的，不是正则表达式独立对象的属性。反之，你应始终将其使用为 `RegExp.rightContext` 或者 `RegExp["$'"]`。

`rightContext` 属性的值是只读的，并且会在匹配成功时修改。

你不能使用属性访问器 (`RegExp.$'`) 来使用简写的别名，因为解析器在这里会将其看做字符串的开始，并抛出 `SyntaxError`。使用 方括号符号来访问属性。

示例
--

### 使用 `rightContext` 和 `$'`

```js
var re = /hello/g;
re.test("hello world!");
RegExp.rightContext; // " world!"
RegExp["$'"]; // " world!"
```

规范
--

非标准。并不是任何现行规范的一部分。

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

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

另见
--

* 非标准
 `RegExp.input ($_)`
* 非标准
 `RegExp.lastMatch ($&)`
* 非标准
 `RegExp.lastParen ($+)`
* 非标准
 `RegExp.leftContext ($`)`
* 非标准
 `RegExp.$1-$9`


get RegExp[@@species] - JavaScript

get RegExp[@@species]
=====================

**`RegExp[@@species]`** 访问器属性返回`RegExp` 的构造器。

语法
--
```

RegExp[Symbol.species]
```

描述
--

`species` 访问器属性返回 `RegExp` 对象的默认构造器。子类构造器可能会覆盖它，来修改构造器的指派。

示例
--

`species`属性返回默认构造器函数，它是用于`RegExp` 对象的`RegExp`构造器：

```js
RegExp[Symbol.species]; // 函数 RegExp()
```

在派生的正则类（也就是你自定义的正则类 `MyRegExp`）中，`MyRegExp` 的 species 是 `MyRegExp` 构造器。但是，你可能希望覆盖它，以便在你的派生类方法中，返回 `RegExp` 父类对象：

```js
class MyRegExp extends RegExp {
  // 将 MyRegExp species 覆盖为 RegExp 父类构造器
  static get [Symbol.species]() {
    return RegExp;
  }
}
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-get-regexp-@@species |

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

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

另见
--

* `RegExp`
* `Symbol.species`


RegExp() constructor - JavaScript

RegExp() constructor
====================

**`RegExp`** 用于创建正则表达式对象，该对象用于将文本与一个模式匹配

阅读JavaScript 指南中的正则表达式一节以了解正则表达式。

尝试一下
----

语法
--

可以使用字面量、构造函数和工厂方法来创建正则表达式
```

/pattern/flags
new RegExp(pattern[, flags])
RegExp(pattern[, flags])
```

### 参数

`pattern`

正则表达式的文本。从 ES5 开始，这也可以是另一个 `RegExp` 对象或文字（仅用于两个 RegExp 构造函数符号）。模式可以包含特殊字符来匹配比字面值字符串更广泛的值范围。

`flags`

如果指定，`flags` 是包含要添加的标志的字符串。

或者，如果为模式提供了一个对象，flags 字符串将替换该对象的任何标志 (并且`lastIndex`将重置为 0)(从 ES2015 开始)。

如果没有指定`flags`并且提供了一个正则表达式对象，则该对象的 flags(和 lastIndex 值) 将被复制。

`flags` 可包含下列任何字符的组合：

`g` （全局匹配）

找到所有的匹配，而不是在第一个匹配之后停止。

`i` （忽略大小写）

如果`u`标志也被启用，使用 Unicode 大小写折叠。

`m` （多行匹配）

将开始和结束字符 (`^` and `$`) 视为在多行上工作。换句话说，匹配每一行的开头或结尾*each* line (由`\n`或者`\r` 分隔)，而不仅仅是整个输入字符串的开头或结尾。

`s` （点号匹配所有字符）

允许`.` 去匹配新的行

`u` （unicode）

Treat `pattern` as a sequence of Unicode code points. (See also Binary strings).

`y` （sticky，粘性匹配）

Matches only from the index indicated by the `lastIndex` property of this regular expression in the target string. Does not attempt to match from any later indexes.

示例
--

### 字面量和构造函数

这里有两种方法创建`RegExp` 对象：*字面量* 和 *构造函数*.

* **文字符号的** 参数用斜杠括起来，不使用引号。
* **构造函数的** 参数不包含在斜杠之间，但使用引号。

以下三个表达式创建相同的正则表达式：

```js
/ab+c/i;
new RegExp(/ab+c/, "i"); // 字面量
new RegExp("ab+c", "i"); // 构造函数
```

当表达式被求值时，文字表示法会导致对正则表达式的编译。当正则表达式保持不变时，请使用字面量表示法。例如，如果使用字面量表示法来构造循环中使用的正则表达式，则不会在每次迭代时重新编译正则表达式。

正则表达式对象的构造函数—例如，new RegExp('ab+c')—会导致正则表达式的运行时编译。当你知道正则表达式模式将发生变化时，或者你不知道该模式，但正在从其他来源 (如用户输入) 获取它时，请使用构造函数。

规范
--

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

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

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

参见
--

* JavaScript 指南中的
* 正则表达式一节
* `String.prototype.match()`
* `String.prototype.replace()`


RegExp.prototype[@@replace]() - JavaScript

RegExp.prototype[@@replace]()
=============================

**`[@@replace]()`** 方法会在一个字符串中用给定的替换器，替换所有符合正则模式的匹配项，并返回替换后的新字符串结果。用来替换的参数可以是一个字符串或是一个针对每次匹配的回调函数。

尝试一下
----

语法
--
```

regexp[Symbol.replace](str, newSubStr|function)
```

### 参数

`str`

正则替换的目标字符串。

`newSubStr (replacement)`

类型为 `String` 的替换器。支持大多数特殊的替换匹配模式；见`String.prototype.replace()`页的Specifying a string as a parameter部分。

`function (replacement)`

生成新的子字符串的回调函数替换器。作用于该函数的参数的详细描述见`String.prototype.replace()`页的 Specifying a function as a parameter 部分。

### 返回值

用替换器替换相应匹配项后的新字符串。

描述
--

如果匹配模式也是`RegExp`对象，这个方法在 `String.prototype.replace()` 的内部调用。例如，下面的两个方法返回相同结果。

```js
"abc".replace(/a/, "A");

/a/[Symbol.replace]("abc", "A");
```

该方法是为了在 RegExp 子类中自定义匹配的替换模式。

如果匹配模式不是一个`RegExp` 对象，`String.prototype.replace()` 就不会调用该方法，也不会创建一个 `RegExp`对象。

示例
--

### 直接调用

这个方法基本可以和 `String.prototype.replace()` 一样使用，不同之处是 `this` 和参数顺序。

```js
var re = /-/g;
var str = "2016-01-01";
var newstr = re[Symbol.replace](str, ".");
console.log(newstr); // 2016.01.01
```

### 在子类中使用 @@replace

`RegExp` 的子类可以覆写 `[@@replace]()`方法来修改默认行为。

```js
class MyRegExp extends RegExp {
  constructor(pattern, flags, count) {
    super(pattern, flags);
    this.count = count;
  }
  [Symbol.replace](str, replacement) {
    // Perform @@replace |count| times.
    var result = str;
    for (var i = 0; i < this.count; i++) {
      result = RegExp.prototype[Symbol.replace].call(this, result, replacement);
    }
    return result;
  }
}

var re = new MyRegExp("\\d", "", 3);
var str = "01234567";
var newstr = str.replace(re, "#"); // String.prototype.replace calls re[@@replace].
console.log(newstr); // ###34567
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-regexp.prototype-@@replace |

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

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

另见
--

* `String.prototype.replace()`
* `RegExp.prototype[@@match]()`
* `RegExp.prototype[@@search]()`
* `RegExp.prototype[@@split]()`
* `RegExp.prototype.exec()`
* `RegExp.prototype.test()`


RegExp.prototype.exec() - JavaScript

RegExp.prototype.exec()
=======================

**`exec()`** 方法在一个指定字符串中执行一个搜索匹配。返回一个结果数组或 `null`。

尝试一下
----

语法
--

```js
exec(str)
```

### 参数

`str`

要匹配正则表达式的字符串。

### 返回值

如果匹配失败，`exec()` 方法返回 `null`，并将正则表达式的 `lastIndex` 重置为 0。

如果匹配成功，`exec()` 方法返回一个数组，并更新正则表达式对象的 `lastIndex` 属性。完全匹配成功的文本将作为返回数组的第一项，从第二项起，后续每项都对应一个匹配的捕获组。数组还具有以下额外的属性：

`index`

匹配到的字符位于原始字符串的基于 0 的索引值。

`input`

匹配的原始字符串。

`groups`

一个命名捕获组对象，其键是名称，值是捕获组。若没有定义命名捕获组，则 `groups` 的值为 `undefined`。参阅捕获组以了解更多信息。

`indices` 可选

此属性仅在设置了 `d` 标志位时存在。它是一个数组，其中每一个元素表示一个子字符串的边界。每个子字符串匹配本身就是一个数组，其中第一个元素表示起始索引，第二个元素表示结束索引。

描述
--

在设置了 `global` 或 `sticky` 标志位的情况下（如 `/foo/g` 或 `/foo/y`），JavaScript `RegExp` 对象是*有状态*的。它们会将上次成功匹配后的位置记录在 `lastIndex` 属性中。使用此特性，`exec()` 可用来对单个字符串中的多次匹配结果进行逐条的遍历（包括捕获到的匹配），而相比之下， `String.prototype.match()` 只会返回匹配到的结果。

在使用 `exec()` 时，`global` 标志位不会在 `sticky` 标志位被设置时生效，而 `match()` 始终会设置 `sticky` 标志位。

`exec()` 是正则表达式的原始方法。许多其他的正则表达式方法会在内部调用 `exec()`——包括一些字符串方法也会调用 `exec()`，如 `@@replace`。虽然 `exec()` 本身非常强大而又有效，但它通常不能最清楚地表示调用的目的。

* 如果你只是为了判断是否匹配，请使用 `RegExp.prototype.test()` 方法代替。
* 如果你只是为了找出所有匹配正则表达式的字符串而又不关心捕获组，请使用 `String.prototype.match()` 方法代替。此外，`String.prototype.matchAll()` 允许你对匹配项进行迭代，这有助于简化匹配字符串的多个部分（带有匹配组）。
* 如果你只是为了查找在字符串中匹配的索引，请使用 `String.prototype.search()` 方法代替。
示例
--

### 使用 exec()

考虑以下示例：

```js
// Match "quick brown" followed by "jumps", ignoring characters in between
// Remember "brown" and "jumps"
// Ignore case
const re = /quick\s(?<color>brown).+?(jumps)/dgi;
const result = re.exec("The Quick Brown Fox Jumps Over The Lazy Dog");
```

下表列出这个脚本的返回值（`result`）：

| 属性 | 值 |
| --- | --- |
| `[0]` | `"Quick Brown Fox Jumps"` |
| `[1]` | `"Brown"` |
| `[2]` | `"Jumps"` |
| `index` | `4` |
| `indices` | `[[4, 25], [10, 15], [20, 25]]``groups: { color: [10, 15 ]}` |
| `input` | `"The Quick Brown Fox Jumps Over The Lazy Dog"` |
| `groups` | `{ color: "brown" }` |

另外，由于正则表达式是全局的（`global`），`re.lastIndex` 会被设置为 `25`。

### 查找所有匹配

当正则表达式设置 `g` 标志位时，可以多次执行 `exec` 方法来查找同一个字符串中的成功匹配。当你这样做时，查找将从正则表达式的 `lastIndex` 属性指定的位置开始。（`test()` 也会更新 `lastIndex` 属性）。注意，即使再次查找的字符串不是原查找字符串时，`lastIndex` 也不会被重置，它依旧会从记录的 `lastIndex` 开始。

例如，你使用下面的脚本：

```js
const myRe = /ab\*/g;
const str = "abbcdefabh";
let myArray;
while ((myArray = myRe.exec(str)) !== null) {
  let msg = `Found ${myArray[0]}. `;
  msg += `Next match starts at ${myRe.lastIndex}`;
  console.log(msg);
}
```

脚本运行结果如下：
```

Found abb. Next match starts at 3
Found ab. Next match starts at 9
```

**警告：** 以下情况会导致匹配变成一个无限循环！

* 不要把正则表达式字面量（或者 `RegExp` 构造函数）放在 `while` 条件表达式里。由于每次迭代时 `lastIndex` 的属性都被重置。
* 请确保设置了全局（`g`）标志位，否则 `lastIndex` 不会被更新。
* 如果正则表达式可以匹配长度为零的字符（例如：`/^/gm`），请手动递增 `lastIndex`，以避免其始终卡在相同的位置。

你也可以将这类的代码替换为 `String.prototype.matchAll()` 以降低出错的可能性。

### 结合 `RegExp` 字面量使用 `exec()`

你也可以直接使用 `exec()` 而不是创建一个 `RegExp` 对象：

```js
const matches = /(hello \S+)/.exec("This is a hello world!");
console.log(matches[1]);
```

运行上面的代码，控制台会输出 `'hello world!'` 字符串。

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-regexp.prototype.exec |

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

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

参见
--

* JavaScript 指南中的正则表达式章节
* `RegExp`


RegExp.prototype.hasIndices - JavaScript

RegExp.prototype.hasIndices
===========================

**`hasIndices`** 访问器属性指示 `d` 标志是否与正则表达式一起使用。

尝试一下
----

描述
--

如果 `d` 标志被使用，则 `RegExp.prototype.hasIndices` 的值是 `true`；否则是 `false`。`d` 标志表示正则表达式匹配的结果应该包含每个捕获组子字符串开始和结束的索引。它不会以任何方式改变正则表达式的解释或匹配行为，它只在匹配的结果中提供额外的信息。

`hasIndices` 的 set 访问器是 `undefined`。你不能直接修改此属性。

示例
--

### 使用 hasIndices

```js
const str1 = "foo bar foo";

const regex1 = /foo/dg;

console.log(regex1.hasIndices); // Output: true

console.log(regex1.exec(str1).indices[0]); // Output: Array [0, 3]
console.log(regex1.exec(str1).indices[0]); // Output: Array [8, 11]

const str2 = "foo bar foo";

const regex2 = /foo/;

console.log(regex2.hasIndices); // Output: false

console.log(regex2.exec(str2).indices); // Output: undefined
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-get-regexp.prototype.hasIndices |

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

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

参见
--

* `RegExp.prototype.lastIndex`
* `RegExp.prototype.exec()`
* `RegExp.prototype.dotAll`
* `RegExp.prototype.global`
* `RegExp.prototype.ignoreCase`
* `RegExp.prototype.multiline`
* `RegExp.prototype.source`
* `RegExp.prototype.sticky`
* `RegExp.prototype.unicode`


RegExp.lastParen ($+) - JavaScript

RegExp.lastParen ($+)
=====================

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

**lastParen** 非标准属性是正则表达式的静态和只读属性，包含匹配到的最后一个子串（如果存在）。`RegExp.$+`是这一属性的别名。

语法
--
```

RegExp.lastParen
RegExp['$+']
```

描述
--

`lastParen` 属性是静态的，不是正则表达式独立对象的属性。反之，你应始终将其使用为 `RegExp.lastParen` 或者 `RegExp['$+']`。

`lastParen` 属性的值是只读的，并且会在匹配成功时修改。

你不能使用属性访问器 (`RegExp.$+`) 来使用简写的别名，因为解析器在这里会将 "+" 看做表达式，并抛出 `SyntaxError` 。使用 方括号符号来访问属性。

示例
--

### 使用 `lastParen` 和 `$+`

```js
var re = /(hi)/g;
re.test("hi there!");
RegExp.lastParen; // "hi"
RegExp["$+"]; // "hi"
```

规范
--

非标准。并不是任何现行规范的一部分。

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

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

另见
--

* 非标准
 `RegExp.input ($_)`
* 非标准
 `RegExp.lastMatch ($&)`
* 非标准
 `RegExp.leftContext ($`)`
* 非标准
 `RegExp.rightContext ($')`
* 非标准
 `RegExp.$1-$9`


RegExp.prototype.ignoreCase - JavaScript

RegExp.prototype.ignoreCase
===========================

概述
--

**`ignoreCase`** 属性表明正则表达式是否使用了 "`i`" 标志。`ignoreCase` 是正则表达式实例的只读属性。

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

描述
--

`ignoreCase` 的值是布尔对象，如果使用了"`i`" 标志，则返回 `true`；否则，返回 `false`。"`i`" 标志意味着在字符串进行匹配时，应该忽略大小写。

你无法直接更改此属性。

示例
--

### 示例：使用 `ignoreCase`

```js
var regex = new RegExp("foo", "i");

console.log(regex.ignoreCase); // true
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-get-regexp.prototype.ignorecase |

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

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

参见
--

* `RegExp.prototype.global`
* `RegExp.prototype.lastIndex`
* `RegExp.prototype.multiline`
* `RegExp.prototype.source`
* `RegExp.prototype.sticky`


RegExp.prototype[@@split]() - JavaScript

RegExp.prototype[@@split]()
===========================

**`[@@split]()`** 方法切割 `String` 对象为一个其子字符串的数组。

语法
--
```

regexp[Symbol.split](str[, limit])
```

### 参数

`str`

切割操作的目标字符串

`limit`

可选。一个为了限制切割数量的特定整数。 `[@@split]()` 防范仍会切割每个匹配正则模式的匹配项，直到切割数量达到该限制数，除非提前切割完字符串。

### 返回值

包含其子字符串的`Array` 。

描述
--

如果切割器是一个`RegExp`对象，这个方法就将在 `String.prototype.split()` 的内部调用。例如，下面的两个方法返回相同结果。

```js
"a-b-c".split(/-/);

/-/[Symbol.split]("a-b-c");
```

这个方法为自定义 `RegExp` 子类中的匹配行为而存在。

如果 str 参数不是一个`RegExp` 对象，`String.prototype.split()` 就不会调用该方法，也不会创建一个 `RegExp` 对象。示例

### 直接调用

这个方法的使用方式和 `String.prototype.split()` 相同，不同之处是 `this` 和参数顺序。

```js
var re = /-/g;
var str = "2016-01-02";
var result = re[Symbol.split](str);
console.log(result); // ["2016", "01", "02"]
```

### 在子类中使用 `@@split`

`RegExp` 的子类可以覆写 `[@@split]()`方法来修改默认行为。

```js
class MyRegExp extends RegExp {
  [Symbol.split](str, limit) {
    var result = RegExp.prototype[Symbol.split].call(this, str, limit);
    return result.map((x) => "(" + x + ")");
  }
}

var re = new MyRegExp("-");
var str = "2016-01-02";
var result = str.split(re); // String.prototype.split calls re[@@split].
console.log(result); // ["(2016)", "(01)", "(02)"]
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-regexp.prototype-@@split |

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

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

另见
--

* `String.prototype.split()`
* `RegExp.prototype[@@match]()`
* `RegExp.prototype[@@replace]()`
* `RegExp.prototype[@@search]()`
* `RegExp.prototype.exec()`
* `RegExp.prototype.test()`


RegExp.leftContext ($`) - JavaScript

RegExp.leftContext ($`)
=======================

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

**leftContext** 非标准属性是正则表达式的静态和只读属性，含有最新匹配的左侧子串。 `RegExp.$`` 是这个属性的别名。

语法
--
```

RegExp.leftContext
RegExp['$`']
```

描述
--

`leftContext` 属性是静态的，不是正则表达式独立对象的属性。反之，你应始终将其使用为 `RegExp.leftContext` 或者 `RegExp['$`']`。

`leftContext` 属性的值是只读的，并且会在匹配成功时修改。

你不能使用属性访问器 (`RegExp.$``) 来使用简写的别名，因为解析器在这里会将其看做模板字符串的开始，并抛出 `SyntaxError` 。使用 方括号符号来访问属性。

示例
--

### 使用 `leftContext` 和 `$``

```js
var re = /world/g;
re.test("hello world!");
RegExp.leftContext; // "hello "
RegExp["$`"]; // "hello "
```

规范
--

非标准。并不是任何现行规范的一部分。

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

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

另见
--

* 非标准
 `RegExp.input ($_)`
* 非标准
 `RegExp.lastMatch ($&)`
* 非标准
 `RegExp.lastParen ($+)`
* 非标准
 `RegExp.rightContext ($')`
* 非标准
 `RegExp.$1-$9`


RegExp.prototype.test() - JavaScript

RegExp.prototype.test()
=======================

**`test()`** 方法执行一个检索，用来查看正则表达式与指定的字符串是否匹配。返回 `true` 或 `false`。

尝试一下
----

语法
--
```

regexObj.test(str)
```

### 参数

`str`

用来与正则表达式匹配的字符串

### 返回值

如果正则表达式与指定的字符串匹配，返回`true`；否则`false`。

描述
--

当你想要知道一个正则表达式是否与指定的字符串匹配时，就可以使用 `test()`（类似于 `String.prototype.search()` 方法），差别在于 test 返回一个布尔值，而 search 返回索引（如果找到）或者 -1（如果没找到）；若想知道更多信息（然而执行比较慢），可使用`exec()` 方法（类似于 `String.prototype.match()` 方法）。和 `exec()` (或者组合使用),一样，在相同的全局正则表达式实例上多次调用`test`将会越过之前的匹配。

示例
--

### 使用 `test()`

一个简单的例子，测试 "hello" 是否包含在字符串的最开始，返回布尔值。

```js
let str = "hello world!";
let result = /^hello/.test(str);
console.log(result);
// true
```

下例打印一条信息，该信息内容取决于是否成功通过指定测试：

```js
function testinput(re, str) {
  var midstring;
  if (re.test(str)) {
    midstring = " contains ";
  } else {
    midstring = " does not contain ";
  }
  console.log(str + midstring + re.source);
}
```

### 当设置全局标志的正则使用`test()`

如果正则表达式设置了全局标志，`test()` 的执行会改变正则表达式 `lastIndex`属性。连续的执行`test()`方法，后续的执行将会从 lastIndex 处开始匹配字符串，(`exec()` 同样改变正则本身的 `lastIndex 属性值`).

下面的实例表现了这种行为：

```js
var regex = /foo/g;

// regex.lastIndex is at 0
regex.test("foo"); // true

// regex.lastIndex is now at 3
regex.test("foo"); // false
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-regexp.prototype.test |

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

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

参见
--

* JavaScript 指南的正则表达式章节
* `RegExp`


RegExp.prototype.compile() - JavaScript

RegExp.prototype.compile()
==========================

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

已废弃的 **`compile()`** 方法被用于在脚本执行过程中（重新）编译正则表达式。与 `RegExp` 构造函数基本一样。

语法
--
```

regexObj.compile(pattern, flags)
```

### 参数

`pattern`

正则表达式的文本。

`flags`

如果指定，标志可以具有以下值的任意组合：

`g`

全局匹配

`i`

忽略大小写

`m`

多行;让开始和结束字符（^ 和 $）工作在多行模式工作（例如，^ 和 $ 可以匹配字符串中每一行的开始和结束（行是由 \n 或 \r 分割的），而不只是整个输入字符串的最开始和最末尾处。

`y`

黏度; 在目标字符串中，只从正则表达式的 lastIndex 属性指定的显示位置开始匹配（并且不试图从任何之后的索引匹配）。

描述
--

不推荐 `compile` 方法。你可以使用 `RegExp` 构造函数来得到相同效果。

示例
--

### `使用 compile()`

以下展示如何用新模式和新标志来重新编译正则表达式。

```js
var regexObj = new RegExp("foo", "gi");
regexObj.compile("new foo", "g");
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-regexp.prototype.compile |

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

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

参见
--

* `RegExp`


RegExp.input ($_) - JavaScript

RegExp.input ($\_)
==================

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

**`input`** 非标准属性是正则表达式静态属性，含有正则表达式所匹配的字符串。`RegExp.$_`是这个属性的别名。

语法
--
```

RegExp.input
RegExp.$_
```

描述
--

`input` 属性是静态的，并不是正则表达式独立对象的属性。反之，你应始终将其使用为 `RegExp.input` 或者 `RegExp.$_`。

当正则表达式上搜索的字符串发生改变，并且字符串匹配时，**`input`** 属性的值会修改。

示例
--

### 使用 `input` 和 `$_`

```js
var re = /hi/g;
re.test("hi there!");
RegExp.input; // "hi there!"
re.test("foo"); // 新测试，不匹配
RegExp.$_; // "hi there!"
re.test("hi world!"); // 新测试，匹配
RegExp.$_; // "hi world!"
```

规范
--

非标准。并不是任何现行规范的一部分。

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

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

另见
--

* 非标准
 `RegExp.lastMatch ($&)`
* 非标准
 `RegExp.lastParen ($+)`
* 非标准
 `RegExp.leftContext ($`)`
* 非标准
 `RegExp.rightContext ($')`
* 非标准
 `RegExp.$1-$9`


RegExp.prototype[@@matchAll]() - JavaScript

RegExp.prototype[@@matchAll]()
==============================

\*\*`[@@matchAll]`\*\*方法返回对字符串使用正则表达式的所有匹配项。

{{EmbedInteractiveExample("pages/js/regexp-prototype-@@matchall.html")}}

语法
--
```

regexp[Symbol.matchAll](str)
```

### 参数

`str`

一个`String`的匹配对象。

### 返回值

一个迭代器。

描述
--

本方法在`String.prototype.matchAll()`中被内部调用。例如，以下两个示例返回相同的结果。

```js
"abc".matchAll(/a/);

/a/[Symbol.matchAll]("abc");
```

本方法用于自定义`RegExp`子类中的匹配行为。

示例
--

### 直接调用

本方法的使用方法几乎与`String.prototype.matchAll()`相同，除了`this` 的不同以及参数顺序的的差异。

```js
var re = /[0-9]+/g;
var str = "2016-01-02";
var result = re[Symbol.matchAll](str);

console.log(Array.from(result, (x) => x[0]));
// ["2016", "01", "02"]
```

### 在子类中使用 `@@matchAll`

`RegExp` 的子类可以重写 `[@@matchAll]()` 方法来修改默认行为。例如，返回一个数组而不是迭代器：

```js
class MyRegExp extends RegExp {
  [Symbol.matchAll](str) {
    var result = RegExp.prototype[Symbol.matchAll].call(this, str);
    if (!result) {
      return null;
    } else {
      return Array.from(result);
    }
  }
}

var re = new MyRegExp("([0-9]+)-([0-9]+)-([0-9]+)", "g");
var str = "2016-01-02|2019-03-07";
var result = str.matchAll(re);
console.log(result[0]); // [ "2016-01-02", "2016", "01", "02" ]
console.log(result[1]); // [ "2019-03-07", "2019", "03", "07" ]
```

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

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

参见
--

* `String.prototype.matchAll()`
* `Symbol.matchAll`


Array.prototype.push() - JavaScript

Array.prototype.push()
======================

**`push()`** 方法将指定的元素添加到数组的末尾，并返回新的数组长度。

尝试一下
----

语法
--

```js
push()
push(element0)
push(element0, element1)
push(element0, element1, /\* … ,\*/ elementN)
```

### 参数

`elementN`

添加到数组末尾的元素。

### 返回值

调用方法的对象的新 `length` 属性。

描述
--

`push()` 方法将值追加到一个数组中。

`Array.prototype.unshift()` 有着和 `push()` 相似的行为，但是其作用于数组的开头。

`push()` 方法是一个修改方法。它改变了 `this` 的内容和长度。如果你希望 `this` 的值保持不变，但返回一个末尾追加了元素的新数组，你可以使用 `arr.concat([element0, element1, /* ... ,*/ elementN])` 来代替。请注意，这些元素需要被包装在一个额外的数组中——否则，如果元素本身是一个数组，由于 `concat()` 的行为，它将被展开而不是作为单个元素添加到原数组的末尾。

`push()` 方法是通用的。它只期望 `this` 值具有 `length` 属性和整数键属性。虽然字符串也是类似数组的，但这个方法不适用于它，因为字符串是不可变的。

示例
--

### 在数组中添加元素

接下来的代码中将会生成包含两个元素的 `sports` 数组，然后向其添加两个元素。`total` 变量将会记录数组的新长度。

```js
const sports = ["soccer", "baseball"];
const total = sports.push("football", "swimming");

console.log(sports); // ['soccer', 'baseball', 'football', 'swimming']
console.log(total); // 4
```

### 合并两个数组

这个示例使用展开语法来将第二个数组的所有元素添加到第一个数组中。

```js
const vegetables = ["parsnip", "potato"];
const moreVegs = ["celery", "beetroot"];

// 合并第二个数组到第一个数组中
vegetables.push(...moreVegs);

console.log(vegetables); // ['parsnip', 'potato', 'celery', 'beetroot']
```

也可以通过使用 `concat()` 方法来合并两个数组。

### 在非数组对象中使用 push()

`push()` 方法会读取 `this` 的 `length` 属性。然后，它将 `this` 的每个索引从 `length` 开始设置，并将参数传递给 `push()`。最后，它将 `length` 设置成之前的长度加上已添加元素的数量。

```js
const arrayLike = {
  length: 3,
  unrelated: "foo",
  2: 4,
};
Array.prototype.push.call(arrayLike, 1, 2);
console.log(arrayLike);
// { '2': 4, '3': 1, '4': 2, length: 5, unrelated: 'foo' }

const plainObj = {};
// 这里没有长度属性，所以长度为 0
Array.prototype.push.call(plainObj, 1, 2);
console.log(plainObj);
// { '0': 1, '1': 2, length: 2 }
```

### 以类似数组的方式使用对象

如上所述，`push` 被有意设计为通用的，我们可以利用这一点。就像这个例子所展示的，`Array.prototype.push` 可以很好的处理对象。

注意，我们没有创建一个数组来存储对象集合。相反，我们将集合存储在对象本身上，并通过 `Array.prototype.push` 的 `call` 来调用该方法，让它认为我们正在处理一个数组——归功于 JavaScript 允许我们以任何我们想要的方式建立执行上下文的方式，这样是可行的。

```js
const obj = {
  length: 0,

  addElem(elem) {
    // obj.length 在每次添加元素时自动增加
    [].push.call(this, elem);
  },
};

// 让我们添加一些空对象来说明
obj.addElem({});
obj.addElem({});
console.log(obj.length); // 2
```

注意，尽管 `obj` 不是一个数组，`push` 方法仍成功地增加了 `obj` 的 `length` 属性，就像我们处理一个真正的数组一样。

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-array.prototype.push |

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

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

参见
--

* `core-js` 中 `Array.prototype.push` 的 Polyfill，并对此方法进行了修复
* 索引集合类
* `Array`
* `Array.prototype.pop()`
* `Array.prototype.shift()`
* `Array.prototype.unshift()`
* `Array.prototype.concat()`
* `Array.prototype.splice()`


Array.prototype.copyWithin() - JavaScript

Array.prototype.copyWithin()
============================

**`copyWithin()`** 方法浅复制数组的一部分到同一数组中的另一个位置，并返回它，不会改变原数组的长度。

尝试一下
----

语法
--

```js
copyWithin(target)
copyWithin(target, start)
copyWithin(target, start, end)
```

### 参数

`target`

序列开始替换的目标位置，以 0 为起始的下标表示，且将被转换为整数

* 负索引将从数组末尾开始计数——如果 `target < 0`，则实际是 `target + array.length`。
* 如果 `target < -array.length`，则使用 `0`。
* 如果 `target >= array.length`，则不会拷贝任何内容。
* 如果 `target` 位于 `start` 之后，则复制只会持续到 `array.length` 结束（换句话说，`copyWithin()` 永远不会扩展数组）。

`start` 可选

要复制的元素序列的起始位置，以 0 为起始的下标表示，且将被转换为整数

* 负索引将从数组末尾开始计数——如果 `start < 0`，则实际是 `start + array.length`。
* 如果省略 `start` 或 `start < -array.length`，则默认为 `0`。
* 如果 `start >= array.length`，则不会拷贝任何内容。

`end` 可选

要复制的元素序列的结束位置，以 0 为起始的下标表示，且将被转换为整数。`copyWithin` 将会拷贝到该位置，但不包括 `end` 这个位置的元素。

* 负索引将从数组末尾开始计数——如果 `end < 0`，则实际是 `end + array.length`。
* 如果 `end < -array.length`，则使用`0`。
* 如果省略 `end` 或 `end >= array.length`，则默认为 `array.length`，这将导致直到数组末尾的所有元素都被复制。
* 如果 `end` 位于 `start` 之前，则不会拷贝任何内容。

### 返回值

改变后的数组。

描述
--

`copyWithin()` 方法的工作原理类似于 C 和 C++ 的 `memmove`，是一种移动数组数据的高性能方法，与 `TypedArray` 的同名方法类似。序列在一次中操作被复制和粘贴；即使复制和粘贴区域重叠，粘贴的序列也将具有复制值。

`copyWithin()` 是修改方法。它不会改变 `this` 指向的对象（数组或类数组）的长度，但会更改其的内容，并在必要时创建新属性或删除现有属性。

`copyWithin()` 方法保留空槽。如果要复制的区域是稀疏的，则原来的空槽会被删除并被替换为拷贝的空槽。

`copyWithin()` 方法是通用的。它只期望 `this` 值具有 `length` 属性和整数键属性。虽然字符串也是类似数组的，但这种方法不适用于它们，因为字符串是不可变的。

示例
--

### 使用 copyWithin()

```js
console.log([1, 2, 3, 4, 5].copyWithin(-2));
// [1, 2, 3, 1, 2]

console.log([1, 2, 3, 4, 5].copyWithin(0, 3));
// [4, 5, 3, 4, 5]

console.log([1, 2, 3, 4, 5].copyWithin(0, 3, 4));
// [4, 2, 3, 4, 5]

console.log([1, 2, 3, 4, 5].copyWithin(-2, -3, -1));
// [1, 2, 3, 3, 4]
```

### 在稀疏数组上使用 copyWithin()

`copyWithin()` 将保留空插槽。

```js
console.log([1, , 3].copyWithin(2, 1, 2)); // [1, empty, empty]
```

### 在非数组对象上调用 copyWithin()

`copyWithin()` 方法读取 `this` 的 `length` 属性，然后操作所涉及的整数索引。

```js
const arrayLike = {
  length: 5,
  3: 1,
};
console.log(Array.prototype.copyWithin.call(arrayLike, 0, 3));
// { '0': 1, '3': 1, length: 5 }
console.log(Array.prototype.copyWithin.call(arrayLike, 3, 1));
// { '0': 1, length: 5 }
// '3' 属性被删除，因为在复制的源中是一个空槽
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-array.prototype.copywithin |

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

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

参见
--

* `core-js` 中 `Array.prototype.copyWithin` 的 polyfill
* 索引集合类
* `Array`
* `TypedArray.prototype.copyWithin()`