私有属性
====

类属性在默认情况下是公有的，但可以使用增加哈希前缀 `#` 的方法来定义私有类字段，这一隐秘封装的类特性由 JavaScript 自身强制执行。

语法
--


```js
class ClassWithPrivateField {
  #privateField;
}

class ClassWithPrivateMethod {
  #privateMethod() {
    return "hello world";
  }
}

class ClassWithPrivateStaticField {
  static #PRIVATE\_STATIC\_FIELD;
}

class ClassWithPrivateStaticMethod {
  static #privateStaticMethod() {
    return "hello world";
  }
}
```

示例
--

### 私有字段

私有字段包括私有实例字段和私有静态字段。

#### 私有实例字段

私有实例字段使用 `#名称`（发音为“哈希名称”）声明，这些名称以 `#` 开头。即 `#` 是名称本身的一部分，声明和访问时也需要加上。私有字段在类声明的构造方法中就可被访问。

从作用域之外引用 `#` 名称、内部在未声明的情况下引用私有字段、或尝试使用 `delete` 移除声明的字段都会抛出语法错误。


```js
class ClassWithPrivateField {
  #privateField;

  constructor() {
    this.#privateField = 42;
    delete this.#privateField;   // 语法错误
    this.#undeclaredField = 444; // 语法错误
  }
}

const instance = new ClassWithPrivateField()
instance.#privateField === 42;   // 语法错误
```


**备注：** 可以使用 `in` 运算符检查私有字段（或私有方法）是否存在。当私有字段或私有方法存在时，运算符返回 `true`，否则返回 `false`。

类似于公有字段，私有字段在构造（construction）基类或调用子类的 `super()` 方法时被添加到类实例中。


```js
class ClassWithPrivateField {
  #privateField;

  constructor() {
    this.#privateField = 42;
  }
}

class SubClass extends ClassWithPrivateField {
  #subPrivateField;

  constructor() {
    super();
    this.#subPrivateField = 23;
  }
}

new SubClass();
// SubClass {#privateField: 42, #subPrivateField: 23}
```


#### 私有静态字段

私有静态字段在解析类结构时被添加到类的构造方法（constructor）中。且静态变量只能被静态方法调用的限制仍然成立。


```js
class ClassWithPrivateStaticField {
  static #PRIVATE\_STATIC\_FIELD;

  static publicStaticMethod() {
    ClassWithPrivateStaticField.#PRIVATE\_STATIC\_FIELD = 42;
    return ClassWithPrivateStaticField.#PRIVATE\_STATIC\_FIELD;
  }
}

console.log(ClassWithPrivateStaticField.publicStaticMethod() === 42);
// true
```


私有静态字段有一个来源限制：只有定义该私有静态字段的类能访问该字段。使用 **`this`** 可能会出现意想不到的行为。在下方的例子中，`this` 是 `SubClass` 类（而不是 `BaseClassWithPrivateStaticField` 类）的引用，所以尝试调用 `SubClass.basePublicStaticMethod()` 会抛出 `TypeError`。


```js
class BaseClassWithPrivateStaticField {
  static #PRIVATE\_STATIC\_FIELD;

  static basePublicStaticMethod() {
    this.#PRIVATE\_STATIC\_FIELD = 42;
    return this.#PRIVATE\_STATIC\_FIELD;
  }
}

class SubClass extends BaseClassWithPrivateStaticField {}

let error = null;

try {
  SubClass.basePublicStaticMethod();
} catch (e) {
  error = e;
}

console.log(error instanceof TypeError);
// true
console.log(error);
// TypeError: Cannot write private member #PRIVATE\_STATIC\_FIELD
// to an object whose class did not declare it
```

### 私有方法

#### 私有实例方法

私有实例方法是类实例上可用的方法，它们的访问方式与私有实例字段相同。


```js
class ClassWithPrivateMethod {
  #privateMethod() {
    return "hello world";
  }

  getPrivateMessage() {
    return this.#privateMethod();
  }
}

const instance = new ClassWithPrivateMethod();
console.log(instance.getPrivateMessage());
// hello world
```


私有实例方法可以是生成器方法、异步方法或异步生成器方法，也可以是私有的 getter 和 setter。


```js
class ClassWithPrivateAccessor {
  #message;

  get #decoratedMessage() {
    return `🎬${this.#message}🛑`;
  }
  set #decoratedMessage(msg) {
    this.#message = msg;
  }

  constructor() {
    this.#decoratedMessage = "hello world";
    console.log(this.#decoratedMessage);
  }
}

new ClassWithPrivateAccessor();
// 🎬hello world🛑
```


#### 私有静态方法

像它们的公有等价方法一样，私有静态方法是在类本身而非类的实例上调用的。像私有静态字段一样，只能从类声明内部访问它们。


```js
class ClassWithPrivateStaticMethod {
  static #privateStaticMethod() {
    return 42;
  }

  static publicStaticMethod1() {
    return ClassWithPrivateStaticMethod.#privateStaticMethod();
  }

  static publicStaticMethod2() {
    return this.#privateStaticMethod();
  }
}

console.log(ClassWithPrivateStaticMethod.publicStaticMethod1() === 42);
// true
console.log(ClassWithPrivateStaticMethod.publicStaticMethod2() === 42);
// true
```


私有静态方法可以是生成器方法，异步方法或异步生成器方法。

前面提到的私有静态字段的限制同样适用于私有静态方法。同样地，使用 **`this`** 可能会出现意想不到的行为。在下方的例子中，`this` 是 `Derived` 类（而不是 `Base` 类）的引用，所以尝试调用 `Derived.publicStaticMethod2()` 会抛出 `TypeError`。


```js
class Base {
  static #privateStaticMethod() {
    return 42;
  }
  static publicStaticMethod1() {
    return Base.#privateStaticMethod();
  }
  static publicStaticMethod2() {
    return this.#privateStaticMethod();
  }
}

class Derived extends Base {}

console.log(Derived.publicStaticMethod1());
// 42
console.log(Derived.publicStaticMethod2());
// TypeError: Cannot read private member #privateStaticMethod
// from an object whose class did not declare it
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # prod-PrivateIdentifier |
| ECMAScript Language Specification # prod-00OK517S |

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

### javascript.classes.private\_class\_fields

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

### javascript.classes.private\_class\_fields\_in

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

### javascript.classes.private\_class\_methods

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

参见
--

* Working with private class features (en-US)
* Public class fields
* The
Semantics of All JS Class Elements
* Public and private class fields
 article at the v8.dev site