Hướng dẫn private property javascript - javascript tài sản riêng

Các trường lớp được công khai theo mặc định, nhưng các thành viên lớp tư nhân có thể được tạo bằng cách sử dụng tiền tố băm

class ClassWithPrivateField {
  #privateField;

  constructor() {
    this.#privateField = 42;
    delete this.#privateField; // Syntax error
    this.#undeclaredField = 444; // Syntax error
  }
}

const instance = new ClassWithPrivateField();
instance.#privateField === 42; // Syntax error
2. Việc đóng gói quyền riêng tư của các tính năng lớp này được thực thi bởi chính JavaScript.

Các thành viên tư nhân không có nguồn gốc từ ngôn ngữ trước khi cú pháp này tồn tại. Trong kế thừa nguyên mẫu, hành vi của nó có thể được mô phỏng bằng các đối tượng

class ClassWithPrivateField {
  #privateField;

  constructor() {
    this.#privateField = 42;
    delete this.#privateField; // Syntax error
    this.#undeclaredField = 444; // Syntax error
  }
}

const instance = new ClassWithPrivateField();
instance.#privateField === 42; // Syntax error
3 hoặc đóng, nhưng chúng không thể so sánh với cú pháp
class ClassWithPrivateField {
  #privateField;

  constructor() {
    this.#privateField = 42;
    delete this.#privateField; // Syntax error
    this.#undeclaredField = 444; // Syntax error
  }
}

const instance = new ClassWithPrivateField();
instance.#privateField === 42; // Syntax error
2 về mặt công thái học.

Cú pháp

class ClassWithPrivate {
  #privateField;
  #privateFieldWithInitializer = 42;

  #privateMethod() {
    // …
  }

  static #privateStaticField;
  static #privateStaticFieldWithInitializer = 42;

  static #privateStaticMethod() {
    // …
  }
}

Sự mô tả

Hầu hết các tính năng lớp có các đối tác riêng của họ:

  • Cánh đồng riêng
  • Phương pháp riêng tư
  • Trường tĩnh riêng
  • Phương pháp tĩnh riêng
  • Getters riêng
  • Setters riêng
  • Getters tĩnh riêng
  • Setters tĩnh riêng

Các tính năng này được gọi chung là thuộc tính tư nhân. Tuy nhiên, các nhà xây dựng không thể riêng tư trong JavaScript. Để ngăn chặn các lớp được xây dựng bên ngoài lớp, bạn phải sử dụng một lá cờ riêng.

Các thuộc tính riêng được khai báo với # tên (phát âm là "Tên băm"), là số nhận dạng được đặt trước với

class ClassWithPrivateField {
  #privateField;

  constructor() {
    this.#privateField = 42;
    delete this.#privateField; // Syntax error
    this.#undeclaredField = 444; // Syntax error
  }
}

const instance = new ClassWithPrivateField();
instance.#privateField === 42; // Syntax error
2. Tiền tố băm là một phần vốn có của tên thuộc tính - bạn có thể vẽ mối quan hệ với quy ước tiền tố gạch dưới cũ
class ClassWithPrivateField {
  #privateField;

  constructor() {
    this.#privateField = 42;
    delete this.#privateField; // Syntax error
    this.#undeclaredField = 444; // Syntax error
  }
}

const instance = new ClassWithPrivateField();
instance.#privateField === 42; // Syntax error
6 - nhưng đó không phải là thuộc tính chuỗi thông thường, vì vậy bạn không thể truy cập linh hoạt với ký hiệu khung.# names (pronounced "hash names"), which are identifiers prefixed with
class ClassWithPrivateField {
  #privateField;

  constructor() {
    this.#privateField = 42;
    delete this.#privateField; // Syntax error
    this.#undeclaredField = 444; // Syntax error
  }
}

const instance = new ClassWithPrivateField();
instance.#privateField === 42; // Syntax error
2. The hash prefix is an inherent part of the property name — you can draw relationship with the old underscore prefix convention
class ClassWithPrivateField {
  #privateField;

  constructor() {
    this.#privateField = 42;
    delete this.#privateField; // Syntax error
    this.#undeclaredField = 444; // Syntax error
  }
}

const instance = new ClassWithPrivateField();
instance.#privateField === 42; // Syntax error
6 — but it's not an ordinary string property, so you can't dynamically access it with the bracket notation.

Đó là một lỗi cú pháp để chỉ các tên

class ClassWithPrivateField {
  #privateField;

  constructor() {
    this.#privateField = 42;
    delete this.#privateField; // Syntax error
    this.#undeclaredField = 444; // Syntax error
  }
}

const instance = new ClassWithPrivateField();
instance.#privateField === 42; // Syntax error
2 từ bên ngoài lớp. Đây cũng là một lỗi cú pháp để chỉ các thuộc tính riêng không được khai báo trong cơ thể lớp hoặc cố gắng loại bỏ các thuộc tính được khai báo bằng
class ClassWithPrivateField {
  #privateField;

  constructor() {
    this.#privateField = 42;
    delete this.#privateField; // Syntax error
    this.#undeclaredField = 444; // Syntax error
  }
}

const instance = new ClassWithPrivateField();
instance.#privateField === 42; // Syntax error
8.

class ClassWithPrivateField {
  #privateField;

  constructor() {
    this.#privateField = 42;
    delete this.#privateField; // Syntax error
    this.#undeclaredField = 444; // Syntax error
  }
}

const instance = new ClassWithPrivateField();
instance.#privateField === 42; // Syntax error

JavaScript, là một ngôn ngữ động, có thể thực hiện kiểm tra thời gian biên dịch này vì cú pháp định danh băm đặc biệt, làm cho nó khác với các thuộc tính bình thường ở cấp độ cú pháp.

Nếu bạn truy cập một tài sản riêng từ một đối tượng không có tài sản,

class ClassWithPrivateField {
  #privateField;

  constructor() {
    this.#privateField = 42;
    delete this.#privateField; // Syntax error
    this.#undeclaredField = 444; // Syntax error
  }
}

const instance = new ClassWithPrivateField();
instance.#privateField === 42; // Syntax error
9 sẽ bị ném, thay vì trả lại
class C {
  #x;

  static getX(obj) {
    return obj.#x;
  }
}

console.log(C.getX(new C())); // undefined
console.log(C.getX({})); // TypeError: Cannot read private member #x from an object whose class did not declare it
0 như các thuộc tính bình thường.

class C {
  #x;

  static getX(obj) {
    return obj.#x;
  }
}

console.log(C.getX(new C())); // undefined
console.log(C.getX({})); // TypeError: Cannot read private member #x from an object whose class did not declare it

Bạn có thể sử dụng toán tử

class C {
  #x;

  static getX(obj) {
    return obj.#x;
  }
}

console.log(C.getX(new C())); // undefined
console.log(C.getX({})); // TypeError: Cannot read private member #x from an object whose class did not declare it
1 để kiểm tra các trường riêng có khả năng bị thiếu (hoặc phương thức riêng). Điều này sẽ trả về
class C {
  #x;

  static getX(obj) {
    return obj.#x;
  }
}

console.log(C.getX(new C())); // undefined
console.log(C.getX({})); // TypeError: Cannot read private member #x from an object whose class did not declare it
2 nếu trường hoặc phương thức riêng tồn tại và
class C {
  #x;

  static getX(obj) {
    return obj.#x;
  }
}

console.log(C.getX(new C())); // undefined
console.log(C.getX({})); // TypeError: Cannot read private member #x from an object whose class did not declare it
3 khác.

Lưu ý một hệ quả của các tên riêng luôn được khai báo trước và không thể bỏ qua: nếu bạn thấy rằng một đối tượng sở hữu một thuộc tính riêng của lớp hiện tại (từ séc

class C {
  #x;

  static getX(obj) {
    return obj.#x;
  }
}

console.log(C.getX(new C())); // undefined
console.log(C.getX({})); // TypeError: Cannot read private member #x from an object whose class did not declare it
4 hoặc
class C {
  #x;

  static getX(obj) {
    return obj.#x;
  }
}

console.log(C.getX(new C())); // undefined
console.log(C.getX({})); // TypeError: Cannot read private member #x from an object whose class did not declare it
1), nó phải sở hữu tất cả các thuộc tính riêng khác. Một đối tượng sở hữu các thuộc tính riêng của một lớp thường có nghĩa là nó được xây dựng bởi lớp đó (mặc dù không phải lúc nào cũng).

Các thuộc tính riêng tư không phải là một phần của mô hình kế thừa nguyên mẫu vì chúng chỉ có thể được truy cập trong cơ thể của lớp hiện tại và không được kế thừa bởi các lớp con. Các thuộc tính riêng có cùng tên trong các lớp khác nhau hoàn toàn khác nhau và không tương tác với nhau. Xem chúng như siêu dữ liệu bên ngoài được gắn vào từng trường hợp, được quản lý bởi lớp.

Ví dụ

Cánh đồng riêng

Phương pháp riêng tư

Trường tĩnh riêng

Phương pháp tĩnh riêng

class ClassWithPrivateField {
  #privateField;

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

class SubClass extends ClassWithPrivateField {
  #subPrivateField;

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

new SubClass();
// SubClass {#subPrivateField: 23}

Getters riêng

class C {
  #x;

  static getX(obj) {
    return obj.#x;
  }
}

console.log(C.getX(new C())); // undefined
console.log(C.getX({})); // TypeError: Cannot read private member #x from an object whose class did not declare it
7 from the
class C {
  #x;

  static getX(obj) {
    return obj.#x;
  }
}

console.log(C.getX(new C())); // undefined
console.log(C.getX({})); // TypeError: Cannot read private member #x from an object whose class did not declare it
8 base class is private to
class C {
  #x;

  static getX(obj) {
    return obj.#x;
  }
}

console.log(C.getX(new C())); // undefined
console.log(C.getX({})); // TypeError: Cannot read private member #x from an object whose class did not declare it
8 and is not accessible from the derived
class ClassWithPrivateField {
  #privateField;

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

class SubClass extends ClassWithPrivateField {
  #subPrivateField;

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

new SubClass();
// SubClass {#subPrivateField: 23}
0.

Setters riêng

Getters tĩnh riêng

class Stamper extends class {
  // A base class whose constructor returns the object it's given
  constructor(obj) {
    return obj;
  }
} {
  // This declaration will "stamp" the private field onto the object
  // returned by the base class constructor
  #stamp = 42;
  static getStamp(obj) {
    return obj.#stamp;
  }
}

const obj = {};
new Stamper(obj);
// `Stamper` calls `Base`, which returns `obj`, so `obj` is
// now the `this` value. `Stamper` then defines `#stamp` on `obj`

console.log(obj); // In some dev tools, it shows {#stamp: 42}
console.log(Stamper.getStamp(obj)); // 42
console.log(obj instanceof Stamper); // false

Setters tĩnh riêng This is a potentially very confusing thing to do. You are generally advised to avoid returning anything from the constructor — especially something unrelated to

class ClassWithPrivateField {
  #privateField;

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

class SubClass extends ClassWithPrivateField {
  #subPrivateField;

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

new SubClass();
// SubClass {#subPrivateField: 23}
1.

Trường tĩnh riêng

Phương pháp tĩnh riêng

class ClassWithPrivateStaticField {
  static #PRIVATE_STATIC_FIELD;

  static publicStaticMethod() {
    ClassWithPrivateStaticField.#PRIVATE_STATIC_FIELD = 42;
    return ClassWithPrivateStaticField.#PRIVATE_STATIC_FIELD;
  }

  publicInstanceMethod() {
    ClassWithPrivateStaticField.#PRIVATE_STATIC_FIELD = 42;
    return ClassWithPrivateStaticField.#PRIVATE_STATIC_FIELD;
  }
}

console.log(ClassWithPrivateStaticField.publicStaticMethod()); // 42
console.log(new ClassWithPrivateStaticField().publicInstanceMethod()); // 42

Getters riêng

class BaseClassWithPrivateStaticField {
  static #PRIVATE_STATIC_FIELD;

  static basePublicStaticMethod() {
    this.#PRIVATE_STATIC_FIELD = 42;
    return this.#PRIVATE_STATIC_FIELD;
  }
}

class SubClass extends BaseClassWithPrivateStaticField {}

try {
  SubClass.basePublicStaticMethod();
} catch (e) {
  console.log(e);
  // TypeError: Cannot write private member #PRIVATE_STATIC_FIELD
  // to an object whose class did not declare it
}

Phương pháp riêng tư

Phương pháp cá nhân riêng

Các phương thức cá thể riêng là các phương thức có sẵn trên các trường hợp lớp có quyền truy cập bị hạn chế theo cách tương tự như các trường thể hiện riêng.

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

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

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

Các phương thức cá thể riêng có thể là các hàm tạo máy phát, async hoặc async. Getters và setter riêng cũng có thể, và tuân theo các yêu cầu cú pháp tương tự như các đối tác getter và setter công khai của họ.

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🛑

Phương pháp tĩnh riêng

Giống như các phương thức tĩnh tương đương công khai của họ, được gọi trên bản thân lớp, không phải là trường hợp của lớp. Giống như các trường tĩnh riêng, chúng chỉ có thể truy cập từ bên trong khai báo lớp.

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

Các phương thức tĩnh riêng có thể là các hàm tạo máy phát, async và async.

Các hạn chế tương tự được đề cập trước đây cho các trường tĩnh riêng cho các phương thức tĩnh riêng và tương tự có thể dẫn đến hành vi bất ngờ khi sử dụng

class ClassWithPrivateField {
  #privateField;

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

class SubClass extends ClassWithPrivateField {
  #subPrivateField;

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

new SubClass();
// SubClass {#subPrivateField: 23}
1. Trong ví dụ sau, khi chúng tôi cố gắng gọi
class Stamper extends class {
  // A base class whose constructor returns the object it's given
  constructor(obj) {
    return obj;
  }
} {
  // This declaration will "stamp" the private field onto the object
  // returned by the base class constructor
  #stamp = 42;
  static getStamp(obj) {
    return obj.#stamp;
  }
}

const obj = {};
new Stamper(obj);
// `Stamper` calls `Base`, which returns `obj`, so `obj` is
// now the `this` value. `Stamper` then defines `#stamp` on `obj`

console.log(obj); // In some dev tools, it shows {#stamp: 42}
console.log(Stamper.getStamp(obj)); // 42
console.log(obj instanceof Stamper); // false
2,
class ClassWithPrivateField {
  #privateField;

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

class SubClass extends ClassWithPrivateField {
  #subPrivateField;

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

new SubClass();
// SubClass {#subPrivateField: 23}
1 đề cập đến lớp
class Stamper extends class {
  // A base class whose constructor returns the object it's given
  constructor(obj) {
    return obj;
  }
} {
  // This declaration will "stamp" the private field onto the object
  // returned by the base class constructor
  #stamp = 42;
  static getStamp(obj) {
    return obj.#stamp;
  }
}

const obj = {};
new Stamper(obj);
// `Stamper` calls `Base`, which returns `obj`, so `obj` is
// now the `this` value. `Stamper` then defines `#stamp` on `obj`

console.log(obj); // In some dev tools, it shows {#stamp: 42}
console.log(Stamper.getStamp(obj)); // 42
console.log(obj instanceof Stamper); // false
4 (không phải lớp
class Stamper extends class {
  // A base class whose constructor returns the object it's given
  constructor(obj) {
    return obj;
  }
} {
  // This declaration will "stamp" the private field onto the object
  // returned by the base class constructor
  #stamp = 42;
  static getStamp(obj) {
    return obj.#stamp;
  }
}

const obj = {};
new Stamper(obj);
// `Stamper` calls `Base`, which returns `obj`, so `obj` is
// now the `this` value. `Stamper` then defines `#stamp` on `obj`

console.log(obj); // In some dev tools, it shows {#stamp: 42}
console.log(Stamper.getStamp(obj)); // 42
console.log(obj instanceof Stamper); // false
5) và do đó gây ra
class ClassWithPrivateField {
  #privateField;

  constructor() {
    this.#privateField = 42;
    delete this.#privateField; // Syntax error
    this.#undeclaredField = 444; // Syntax error
  }
}

const instance = new ClassWithPrivateField();
instance.#privateField === 42; // Syntax error
9.

class ClassWithPrivateField {
  #privateField;

  constructor() {
    this.#privateField = 42;
    delete this.#privateField; // Syntax error
    this.#undeclaredField = 444; // Syntax error
  }
}

const instance = new ClassWithPrivateField();
instance.#privateField === 42; // Syntax error
0

Mô phỏng các nhà xây dựng tư nhân

Nhiều ngôn ngữ khác bao gồm khả năng đánh dấu một hàm tạo là riêng tư, điều này ngăn không cho lớp được khởi tạo bên ngoài lớp - bạn chỉ có thể sử dụng các phương thức nhà máy tĩnh tạo ra các thể hiện hoặc không thể tạo các phiên bản. JavaScript không có cách tự nhiên để làm điều này, nhưng nó có thể được thực hiện bằng cách sử dụng cờ tĩnh riêng.

class ClassWithPrivateField {
  #privateField;

  constructor() {
    this.#privateField = 42;
    delete this.#privateField; // Syntax error
    this.#undeclaredField = 444; // Syntax error
  }
}

const instance = new ClassWithPrivateField();
instance.#privateField === 42; // Syntax error
1

Thông số kỹ thuật

Sự chỉ rõ
Đặc tả ngôn ngữ Ecmascript # Prod-PrivateIdentifier
# prod-PrivateIdentifier

Tính tương thích của trình duyệt web

Bảng BCD chỉ tải trong trình duyệt

Xem thêm