Siêu lớp và lớp con trong javascript

Trong cuốn sách này, phong cách lập trình hướng đối tượng (OOP) của JavaScript được giới thiệu theo bốn bước. Chương này bao gồm bước 3 và 4, chương trước bao gồm bước 1 và 2. Các bước là (hình. 12)

  1. Đối tượng đơn lẻ (chương trước). Làm cách nào để các đối tượng, các khối xây dựng OOP cơ bản của JavaScript, hoạt động độc lập?
  2. Chuỗi nguyên mẫu (chương trước). Mỗi đối tượng có một chuỗi không hoặc nhiều đối tượng nguyên mẫu. Nguyên mẫu là cơ chế kế thừa cốt lõi của JavaScript
  3. Lớp học (chương này). Các lớp của JavaScript là nhà máy cho các đối tượng. Mối quan hệ giữa một lớp và các thể hiện của nó dựa trên sự kế thừa nguyên mẫu (bước 2)
  4. Phân lớp (chương này). Mối quan hệ giữa lớp con và lớp cha của nó cũng dựa trên sự kế thừa nguyên mẫu
Hình 12. Cuốn sách này giới thiệu lập trình hướng đối tượng trong JavaScript theo bốn bước

29. 1 Tờ ăn gian. các lớp học

siêu lớp

class Person {
  #firstName; // (A)
  constructor(firstName) {
    this.#firstName = firstName; // (B)
  }
  describe() {
    return `Person named ${this.#firstName}`;
  }
  static extractNames(persons) {
    return persons.map(person => person.#firstName);
  }
}
const tarzan = new Person('Tarzan');
assert.equal(
  tarzan.describe(),
  'Person named Tarzan'
);
assert.deepEqual(
  Person.extractNames([tarzan, new Person('Cheeta')]),
  ['Tarzan', 'Cheeta']
);

phân lớp

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);

ghi chú

  • class Employee extends Person {
      constructor(firstName, title) {
        super(firstName);
        this.title = title; // (C)
      }
      describe() {
        return super.describe() +
          ` (${this.title})`;
      }
    }
    
    const jane = new Employee('Jane', 'CTO');
    assert.equal(
      jane.title,
      'CTO'
    );
    assert.equal(
      jane.describe(),
      'Person named Jane (CTO)'
    );
    13 là trường riêng và phải được khai báo (dòng A) trước khi có thể khởi tạo (dòng B)
    • Một trường riêng tư chỉ có thể được truy cập bên trong lớp xung quanh nó. Nó thậm chí không thể được truy cập bởi các lớp con
  • class Employee extends Person {
      constructor(firstName, title) {
        super(firstName);
        this.title = title; // (C)
      }
      describe() {
        return super.describe() +
          ` (${this.title})`;
      }
    }
    
    const jane = new Employee('Jane', 'CTO');
    assert.equal(
      jane.title,
      'CTO'
    );
    assert.equal(
      jane.describe(),
      'Person named Jane (CTO)'
    );
    14 là một thuộc tính và có thể được khởi tạo mà không cần khai báo trước (dòng C). JavaScript tương đối thường công khai dữ liệu cá thể (ngược lại với e. g. , Java thích ẩn nó hơn)

29. 2 Các yếu tố cần thiết của lớp học

Các lớp về cơ bản là một cú pháp nhỏ gọn để thiết lập các chuỗi nguyên mẫu (đã được giải thích trong chương trước). Về cơ bản, các lớp của JavaScript là độc đáo. Nhưng đó là điều mà chúng tôi ít thấy khi làm việc với họ. Họ thường cảm thấy quen thuộc với những người đã sử dụng các ngôn ngữ lập trình hướng đối tượng khác

Lưu ý rằng chúng ta không cần các lớp để tạo các đối tượng. Chúng tôi cũng có thể làm như vậy thông qua đối tượng chữ. Đó là lý do tại sao mẫu đơn không cần thiết trong JavaScript và các lớp được sử dụng ít hơn so với nhiều ngôn ngữ khác có chúng

29. 2. Hạng 1 A dành cho người

Trước đây chúng tôi đã làm việc với

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
15 và
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
16, các đối tượng đơn lẻ đại diện cho người. Hãy sử dụng một khai báo lớp để triển khai một nhà máy cho các đối tượng đó

class Person {
  #firstName; // (A)
  constructor(firstName) {
    this.#firstName = firstName; // (B)
  }
  describe() {
    return `Person named ${this.#firstName}`;
  }
  static extractNames(persons) {
    return persons.map(person => person.#firstName);
  }
}

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
15 và
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
16 hiện có thể được tạo thông qua
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
19

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
0

Hãy kiểm tra những gì bên trong cơ thể của lớp

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
20

  • class Employee extends Person {
      constructor(firstName, title) {
        super(firstName);
        this.title = title; // (C)
      }
      describe() {
        return super.describe() +
          ` (${this.title})`;
      }
    }
    
    const jane = new Employee('Jane', 'CTO');
    assert.equal(
      jane.title,
      'CTO'
    );
    assert.equal(
      jane.describe(),
      'Person named Jane (CTO)'
    );
    21 là một phương thức đặc biệt được gọi sau khi tạo một thể hiện mới. Bên trong nó,
    class Employee extends Person {
      constructor(firstName, title) {
        super(firstName);
        this.title = title; // (C)
      }
      describe() {
        return super.describe() +
          ` (${this.title})`;
      }
    }
    
    const jane = new Employee('Jane', 'CTO');
    assert.equal(
      jane.title,
      'CTO'
    );
    assert.equal(
      jane.describe(),
      'Person named Jane (CTO)'
    );
    22 đề cập đến trường hợp đó

  • [ES2022]

    class Employee extends Person {
      constructor(firstName, title) {
        super(firstName);
        this.title = title; // (C)
      }
      describe() {
        return super.describe() +
          ` (${this.title})`;
      }
    }
    
    const jane = new Employee('Jane', 'CTO');
    assert.equal(
      jane.title,
      'CTO'
    );
    assert.equal(
      jane.describe(),
      'Person named Jane (CTO)'
    );
    13 là trường riêng của một cá thể. Các trường như vậy được lưu trữ trong các trường hợp. Chúng được truy cập tương tự như các thuộc tính, nhưng tên của chúng là riêng biệt – chúng luôn bắt đầu bằng các ký hiệu băm (
    class Employee extends Person {
      constructor(firstName, title) {
        super(firstName);
        this.title = title; // (C)
      }
      describe() {
        return super.describe() +
          ` (${this.title})`;
      }
    }
    
    const jane = new Employee('Jane', 'CTO');
    assert.equal(
      jane.title,
      'CTO'
    );
    assert.equal(
      jane.describe(),
      'Person named Jane (CTO)'
    );
    24). Và họ vô hình với thế giới bên ngoài lớp học

    class Employee extends Person {
      constructor(firstName, title) {
        super(firstName);
        this.title = title; // (C)
      }
      describe() {
        return super.describe() +
          ` (${this.title})`;
      }
    }
    
    const jane = new Employee('Jane', 'CTO');
    assert.equal(
      jane.title,
      'CTO'
    );
    assert.equal(
      jane.describe(),
      'Person named Jane (CTO)'
    );
    6

    Trước khi chúng ta có thể khởi tạo

    class Employee extends Person {
      constructor(firstName, title) {
        super(firstName);
        this.title = title; // (C)
      }
      describe() {
        return super.describe() +
          ` (${this.title})`;
      }
    }
    
    const jane = new Employee('Jane', 'CTO');
    assert.equal(
      jane.title,
      'CTO'
    );
    assert.equal(
      jane.describe(),
      'Person named Jane (CTO)'
    );
    13 trong hàm tạo (dòng B), chúng ta cần khai báo nó bằng cách đề cập đến nó trong phần thân của lớp (dòng A)

  • class Employee extends Person {
      constructor(firstName, title) {
        super(firstName);
        this.title = title; // (C)
      }
      describe() {
        return super.describe() +
          ` (${this.title})`;
      }
    }
    
    const jane = new Employee('Jane', 'CTO');
    assert.equal(
      jane.title,
      'CTO'
    );
    assert.equal(
      jane.describe(),
      'Person named Jane (CTO)'
    );
    26 là một phương thức. Nếu chúng ta gọi nó thông qua
    class Employee extends Person {
      constructor(firstName, title) {
        super(firstName);
        this.title = title; // (C)
      }
      describe() {
        return super.describe() +
          ` (${this.title})`;
      }
    }
    
    const jane = new Employee('Jane', 'CTO');
    assert.equal(
      jane.title,
      'CTO'
    );
    assert.equal(
      jane.describe(),
      'Person named Jane (CTO)'
    );
    27 thì
    class Employee extends Person {
      constructor(firstName, title) {
        super(firstName);
        this.title = title; // (C)
      }
      describe() {
        return super.describe() +
          ` (${this.title})`;
      }
    }
    
    const jane = new Employee('Jane', 'CTO');
    assert.equal(
      jane.title,
      'CTO'
    );
    assert.equal(
      jane.describe(),
      'Person named Jane (CTO)'
    );
    22 đề cập đến
    class Employee extends Person {
      constructor(firstName, title) {
        super(firstName);
        this.title = title; // (C)
      }
      describe() {
        return super.describe() +
          ` (${this.title})`;
      }
    }
    
    const jane = new Employee('Jane', 'CTO');
    assert.equal(
      jane.title,
      'CTO'
    );
    assert.equal(
      jane.describe(),
      'Person named Jane (CTO)'
    );
    29 bên trong cơ thể của
    class Employee extends Person {
      constructor(firstName, title) {
        super(firstName);
        this.title = title; // (C)
      }
      describe() {
        return super.describe() +
          ` (${this.title})`;
      }
    }
    
    const jane = new Employee('Jane', 'CTO');
    assert.equal(
      jane.title,
      'CTO'
    );
    assert.equal(
      jane.describe(),
      'Person named Jane (CTO)'
    );
    26

    class Employee extends Person {
      constructor(firstName, title) {
        super(firstName);
        this.title = title; // (C)
      }
      describe() {
        return super.describe() +
          ` (${this.title})`;
      }
    }
    
    const jane = new Employee('Jane', 'CTO');
    assert.equal(
      jane.title,
      'CTO'
    );
    assert.equal(
      jane.describe(),
      'Person named Jane (CTO)'
    );
    3

  • class Employee extends Person {
      constructor(firstName, title) {
        super(firstName);
        this.title = title; // (C)
      }
      describe() {
        return super.describe() +
          ` (${this.title})`;
      }
    }
    
    const jane = new Employee('Jane', 'CTO');
    assert.equal(
      jane.title,
      'CTO'
    );
    assert.equal(
      jane.describe(),
      'Person named Jane (CTO)'
    );
    31 là một phương thức tĩnh. "Tĩnh" có nghĩa là nó thuộc về lớp, không thuộc về thể hiện

    class Employee extends Person {
      constructor(firstName, title) {
        super(firstName);
        this.title = title; // (C)
      }
      describe() {
        return super.describe() +
          ` (${this.title})`;
      }
    }
    
    const jane = new Employee('Jane', 'CTO');
    assert.equal(
      jane.title,
      'CTO'
    );
    assert.equal(
      jane.describe(),
      'Person named Jane (CTO)'
    );
    5

Chúng ta cũng có thể tạo các thuộc tính thể hiện (trường công khai) trong hàm tạo

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
6

Ngược lại với các trường riêng của cá thể, các thuộc tính của cá thể không cần phải khai báo trong thân lớp

29. 2. 2 Biểu thức lớp

Có hai loại định nghĩa lớp (cách định nghĩa lớp)

  • Khai báo lớp, mà chúng ta đã thấy trong phần trước
  • Biểu thức lớp, mà chúng ta sẽ thấy tiếp theo

Các biểu thức lớp có thể ẩn danh và được đặt tên

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
7

Tên của biểu thức lớp được đặt tên hoạt động tương tự như tên của biểu thức hàm được đặt tên. Nó chỉ có thể được truy cập bên trong phần thân của một lớp và giữ nguyên, bất kể lớp đó được gán cho cái gì

29. 2. 3 Nhà điều hành
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
32

Toán tử

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
32 cho chúng ta biết nếu một giá trị là một thể hiện của một lớp nhất định

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
0

Chúng ta sẽ khám phá toán tử

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
32 chi tiết hơn sau, sau khi chúng ta xem xét phân lớp

29. 2. 4 Vị trí công cộng (thuộc tính) so với. khe riêng

Trong ngôn ngữ JavaScript, các đối tượng có thể có hai loại “khe”

  • Các vị trí công khai (còn được gọi là thuộc tính). Ví dụ: các phương thức là các vị trí công khai
  • Vị trí riêng tư [ES2022]. Ví dụ: các trường riêng tư là các vị trí riêng tư

Đây là những quy tắc quan trọng nhất mà chúng ta cần biết về thuộc tính và vị trí riêng tư

  • Trong các lớp, chúng ta có thể sử dụng các phiên bản công khai và riêng tư của các trường, phương thức, getters và setters. Tất cả chúng đều là các khe trong các đối tượng. Chúng được đặt trong đối tượng nào tùy thuộc vào việc từ khóa
    class Employee extends Person {
      constructor(firstName, title) {
        super(firstName);
        this.title = title; // (C)
      }
      describe() {
        return super.describe() +
          ` (${this.title})`;
      }
    }
    
    const jane = new Employee('Jane', 'CTO');
    assert.equal(
      jane.title,
      'CTO'
    );
    assert.equal(
      jane.describe(),
      'Person named Jane (CTO)'
    );
    35 có được sử dụng hay không và các yếu tố khác
  • Một getter và một setter có cùng khóa sẽ tạo một khe truy cập duy nhất. Một Accessor cũng có thể chỉ có một getter hoặc chỉ một setter
  • Thuộc tính và vị trí riêng tư rất khác nhau – ví dụ

Thông tin thêm về thuộc tính và vị trí riêng tư

Chương này không đề cập đến tất cả các chi tiết về tài sản và vị trí riêng tư (chỉ những điều cần thiết). Nếu bạn muốn tìm hiểu sâu hơn, bạn có thể làm như vậy tại đây

Lớp sau minh họa hai loại vị trí. Mỗi trường hợp của nó có một trường riêng và một thuộc tính

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
0

Đúng như dự đoán, bên ngoài

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
36, chúng tôi chỉ có thể nhìn thấy tài sản

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
1

Tiếp theo, chúng ta sẽ xem xét một số chi tiết về các vị trí riêng tư

29. 2. Chi tiết hơn về các vị trí 5 Private [ES2022] (nâng cao)

29. 2. 5. 1 Không thể truy cập các vị trí riêng tư trong các lớp con

Một khe riêng thực sự chỉ có thể được truy cập bên trong phần thân của lớp của nó. Chúng tôi thậm chí không thể truy cập nó từ một lớp con

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
2

Subclassing via

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
37 is explained later in this chapter. How to work around this limitation is explained in §29. 5. 4 “Simulating protected visibility and friend visibility via WeakMaps”

29. 2. 5. 2 Each private slot has a unique key (a private name)

Các khe riêng có các phím duy nhất tương tự như các biểu tượng. Consider the following class from earlier

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
3

Internally, the private field of

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
36 is handled roughly like this

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
4

The value of

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
39 is called a private name. We can’t use private names directly in JavaScript, we can only use them indirectly, via the fixed identifiers of private fields, private methods, and private accessors. Where the fixed identifiers of public slots (such as
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
40) are interpreted as string keys, the fixed identifiers of private slots (such as
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
41) refer to private names (similarly to how variable names refer to values)

29. 2. 5. 3 The same private identifier refers to different private names in different classes

Because the identifiers of private slots aren’t used as keys, using the same identifier in different classes produces different slots (line A and line C)

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
5

29. 2. 5. 4 The names of private fields never clash

Even if a subclass uses the same name for a private field, the two names never clash because they refer to private names (which are always unique). In the following example,

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
42 in
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
43 does not clash with
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
42 in
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
45, even though both slots are stored directly in
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
46

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
6

Subclassing via

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
37 is explained later in this chapter

29. 2. 5. 5 Sử dụng
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
48 để kiểm tra xem một đối tượng có một vị trí riêng không

The

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
48 operator can be used to check if a private slot exists (line A)

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
7

Hãy xem thêm các ví dụ về

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
48 được áp dụng cho các vị trí riêng tư

phương pháp riêng tư. Đoạn mã sau cho thấy các phương thức riêng tư tạo các vị trí riêng tư trong các phiên bản

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
8

Trường riêng tĩnh. Chúng tôi cũng có thể sử dụng

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
48 cho trường riêng tĩnh

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
9

Phương thức riêng tư tĩnh. Và chúng ta có thể kiểm tra vị trí của một phương thức riêng tư tĩnh

class Person {
  #firstName; // (A)
  constructor(firstName) {
    this.#firstName = firstName; // (B)
  }
  describe() {
    return `Person named ${this.#firstName}`;
  }
  static extractNames(persons) {
    return persons.map(person => person.#firstName);
  }
}
0

Sử dụng cùng một định danh riêng trong các lớp khác nhau. Trong ví dụ tiếp theo, hai lớp

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
52 và
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
20 đều có một vị trí có mã định danh là
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
54. Toán tử
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
48 phân biệt chúng một cách chính xác

class Person {
  #firstName; // (A)
  constructor(firstName) {
    this.#firstName = firstName; // (B)
  }
  describe() {
    return `Person named ${this.#firstName}`;
  }
  static extractNames(persons) {
    return persons.map(person => person.#firstName);
  }
}
1

29. 2. 6 Ưu nhược điểm của class trong JavaScript

Tôi khuyên bạn nên sử dụng các lớp vì những lý do sau

  • Các lớp là một tiêu chuẩn chung để tạo và kế thừa đối tượng hiện được hỗ trợ rộng rãi trên các thư viện và khung. Đây là một cải tiến so với trước đây, khi hầu hết mọi framework đều có thư viện kế thừa riêng

  • Họ trợ giúp các công cụ như IDE và trình kiểm tra loại trong công việc của họ và kích hoạt các tính năng mới ở đó

  • Nếu bạn đến từ một ngôn ngữ khác với JavaScript và đã quen với các lớp học, thì bạn có thể bắt đầu nhanh hơn

  • Công cụ JavaScript tối ưu hóa chúng. Nghĩa là, mã sử dụng các lớp hầu như luôn nhanh hơn mã sử dụng thư viện kế thừa tùy chỉnh

  • Chúng ta có thể phân lớp các hàm dựng sẵn như

    class Employee extends Person {
      constructor(firstName, title) {
        super(firstName);
        this.title = title; // (C)
      }
      describe() {
        return super.describe() +
          ` (${this.title})`;
      }
    }
    
    const jane = new Employee('Jane', 'CTO');
    assert.equal(
      jane.title,
      'CTO'
    );
    assert.equal(
      jane.describe(),
      'Person named Jane (CTO)'
    );
    56

Điều đó không có nghĩa là các lớp học là hoàn hảo

  • Có nguy cơ lạm dụng quyền thừa kế

  • Có nguy cơ đưa quá nhiều chức năng vào các lớp (khi một số chức năng thường được đưa vào các chức năng tốt hơn)

  • Các lớp trông quen thuộc với các lập trình viên đến từ các ngôn ngữ khác, nhưng chúng hoạt động khác và được sử dụng khác (xem tiểu mục tiếp theo). Do đó, có nguy cơ những lập trình viên viết mã không giống như JavaScript

  • Cách các lớp dường như hoạt động bề ngoài hoàn toàn khác với cách chúng thực sự hoạt động. Nói cách khác, có sự mất kết nối giữa cú pháp và ngữ nghĩa. Hai ví dụ là

    • Một định nghĩa phương thức bên trong một lớp
      class Employee extends Person {
        constructor(firstName, title) {
          super(firstName);
          this.title = title; // (C)
        }
        describe() {
          return super.describe() +
            ` (${this.title})`;
        }
      }
      
      const jane = new Employee('Jane', 'CTO');
      assert.equal(
        jane.title,
        'CTO'
      );
      assert.equal(
        jane.describe(),
        'Person named Jane (CTO)'
      );
      57 tạo một phương thức trong đối tượng
      class Employee extends Person {
        constructor(firstName, title) {
          super(firstName);
          this.title = title; // (C)
        }
        describe() {
          return super.describe() +
            ` (${this.title})`;
        }
      }
      
      const jane = new Employee('Jane', 'CTO');
      assert.equal(
        jane.title,
        'CTO'
      );
      assert.equal(
        jane.describe(),
        'Person named Jane (CTO)'
      );
      58
    • Các lớp là các chức năng

    Động lực cho việc ngắt kết nối là khả năng tương thích ngược. Rất may, việc ngắt kết nối gây ra một số vấn đề trong thực tế;

Đây là cái nhìn đầu tiên về các lớp học. Chúng tôi sẽ sớm khám phá nhiều tính năng hơn

Tập thể dục. Viết một lớp học

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
59

29. 2. 7 Mẹo sử dụng lớp học

  • Sử dụng tính kế thừa một cách tiết kiệm – nó có xu hướng làm cho mã phức tạp hơn và trải rộng các chức năng liên quan trên nhiều vị trí
  • Thay vì các thành viên tĩnh, tốt hơn là sử dụng các hàm và biến bên ngoài. Chúng tôi thậm chí có thể đặt những thứ đó ở chế độ riêng tư thành một mô-đun, đơn giản bằng cách không xuất chúng. Hai ngoại lệ quan trọng đối với quy tắc này là
  • Chỉ đặt chức năng cốt lõi trong các phương thức nguyên mẫu. Các chức năng khác được triển khai tốt hơn thông qua các hàm - đặc biệt là các thuật toán liên quan đến các thể hiện của nhiều lớp

29. 3 Nội bộ của các lớp

29. 3. Lớp 1 A thực sự là hai đối tượng được kết nối

Về cơ bản, một lớp trở thành hai đối tượng được kết nối. Hãy xem lại lớp

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
20 để xem nó hoạt động như thế nào

class Person {
  #firstName; // (A)
  constructor(firstName) {
    this.#firstName = firstName; // (B)
  }
  describe() {
    return `Person named ${this.#firstName}`;
  }
  static extractNames(persons) {
    return persons.map(person => person.#firstName);
  }
}
2

Đối tượng đầu tiên được tạo bởi lớp được lưu trữ trong

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
20. Nó có bốn thuộc tính

class Person {
  #firstName; // (A)
  constructor(firstName) {
    this.#firstName = firstName; // (B)
  }
  describe() {
    return `Person named ${this.#firstName}`;
  }
  static extractNames(persons) {
    return persons.map(person => person.#firstName);
  }
}
3

Hai tài sản còn lại là

  • class Employee extends Person {
      constructor(firstName, title) {
        super(firstName);
        this.title = title; // (C)
      }
      describe() {
        return super.describe() +
          ` (${this.title})`;
      }
    }
    
    const jane = new Employee('Jane', 'CTO');
    assert.equal(
      jane.title,
      'CTO'
    );
    assert.equal(
      jane.describe(),
      'Person named Jane (CTO)'
    );
    62 là phương thức tĩnh mà chúng ta đã thấy trong thực tế
  • class Employee extends Person {
      constructor(firstName, title) {
        super(firstName);
        this.title = title; // (C)
      }
      describe() {
        return super.describe() +
          ` (${this.title})`;
      }
    }
    
    const jane = new Employee('Jane', 'CTO');
    assert.equal(
      jane.title,
      'CTO'
    );
    assert.equal(
      jane.describe(),
      'Person named Jane (CTO)'
    );
    63 trỏ đến đối tượng thứ hai được tạo bởi một định nghĩa lớp

Đây là nội dung của

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
63

class Person {
  #firstName; // (A)
  constructor(firstName) {
    this.#firstName = firstName; // (B)
  }
  describe() {
    return `Person named ${this.#firstName}`;
  }
  static extractNames(persons) {
    return persons.map(person => person.#firstName);
  }
}
4

Có hai thuộc tính

  • class Employee extends Person {
      constructor(firstName, title) {
        super(firstName);
        this.title = title; // (C)
      }
      describe() {
        return super.describe() +
          ` (${this.title})`;
      }
    }
    
    const jane = new Employee('Jane', 'CTO');
    assert.equal(
      jane.title,
      'CTO'
    );
    assert.equal(
      jane.describe(),
      'Person named Jane (CTO)'
    );
    65 trỏ đến hàm tạo
  • class Employee extends Person {
      constructor(firstName, title) {
        super(firstName);
        this.title = title; // (C)
      }
      describe() {
        return super.describe() +
          ` (${this.title})`;
      }
    }
    
    const jane = new Employee('Jane', 'CTO');
    assert.equal(
      jane.title,
      'CTO'
    );
    assert.equal(
      jane.describe(),
      'Person named Jane (CTO)'
    );
    66 là phương pháp mà chúng tôi đã sử dụng

29. 3. 2 Các lớp thiết lập chuỗi nguyên mẫu cho các phiên bản của chúng

Đối tượng

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
63 là nguyên mẫu của tất cả các phiên bản

class Person {
  #firstName; // (A)
  constructor(firstName) {
    this.#firstName = firstName; // (B)
  }
  describe() {
    return `Person named ${this.#firstName}`;
  }
  static extractNames(persons) {
    return persons.map(person => person.#firstName);
  }
}
5

Điều đó giải thích cách các cá thể nhận được các phương thức của chúng. Họ kế thừa chúng từ đối tượng

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
63

Quả sung. 13 hình dung cách mọi thứ được kết nối

Hình 13. Lớp
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
20 có thuộc tính
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
70 trỏ đến một đối tượng là nguyên mẫu của tất cả các phiên bản của
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
20. Các đối tượng
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
15 và
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
16 là hai trường hợp như vậy

29. 3. 3 ____174 so với.
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
70

Rất dễ nhầm lẫn giữa

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
74 và
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
70. Hy vọng, hình. 13 nói rõ chúng khác nhau như thế nào

  • class Employee extends Person {
      constructor(firstName, title) {
        super(firstName);
        this.title = title; // (C)
      }
      describe() {
        return super.describe() +
          ` (${this.title})`;
      }
    }
    
    const jane = new Employee('Jane', 'CTO');
    assert.equal(
      jane.title,
      'CTO'
    );
    assert.equal(
      jane.describe(),
      'Person named Jane (CTO)'
    );
    74 là một bộ truy cập của lớp
    class Employee extends Person {
      constructor(firstName, title) {
        super(firstName);
        this.title = title; // (C)
      }
      describe() {
        return super.describe() +
          ` (${this.title})`;
      }
    }
    
    const jane = new Employee('Jane', 'CTO');
    assert.equal(
      jane.title,
      'CTO'
    );
    assert.equal(
      jane.describe(),
      'Person named Jane (CTO)'
    );
    79 cho phép chúng tôi lấy và đặt các nguyên mẫu của các thể hiện của nó

  • class Employee extends Person {
      constructor(firstName, title) {
        super(firstName);
        this.title = title; // (C)
      }
      describe() {
        return super.describe() +
          ` (${this.title})`;
      }
    }
    
    const jane = new Employee('Jane', 'CTO');
    assert.equal(
      jane.title,
      'CTO'
    );
    assert.equal(
      jane.describe(),
      'Person named Jane (CTO)'
    );
    70 là một tài sản bình thường như bao tài sản khác. Nó chỉ đặc biệt vì toán tử
    class Employee extends Person {
      constructor(firstName, title) {
        super(firstName);
        this.title = title; // (C)
      }
      describe() {
        return super.describe() +
          ` (${this.title})`;
      }
    }
    
    const jane = new Employee('Jane', 'CTO');
    assert.equal(
      jane.title,
      'CTO'
    );
    assert.equal(
      jane.describe(),
      'Person named Jane (CTO)'
    );
    81 sử dụng giá trị của nó làm nguyên mẫu của các thể hiện. Tên của nó không phải là lý tưởng. Một tên khác chẳng hạn như
    class Employee extends Person {
      constructor(firstName, title) {
        super(firstName);
        this.title = title; // (C)
      }
      describe() {
        return super.describe() +
          ` (${this.title})`;
      }
    }
    
    const jane = new Employee('Jane', 'CTO');
    assert.equal(
      jane.title,
      'CTO'
    );
    assert.equal(
      jane.describe(),
      'Person named Jane (CTO)'
    );
    82 sẽ phù hợp hơn

29. 3. 4 
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
65 (nâng cao)

Có một chi tiết trong hình. 13 mà chúng tôi chưa xem xét.

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
65 điểm trở lại
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
20

class Person {
  #firstName; // (A)
  constructor(firstName) {
    this.#firstName = firstName; // (B)
  }
  describe() {
    return `Person named ${this.#firstName}`;
  }
  static extractNames(persons) {
    return persons.map(person => person.#firstName);
  }
}
6

Thiết lập này tồn tại do khả năng tương thích ngược. Nhưng nó có hai lợi ích bổ sung

Đầu tiên, mỗi thể hiện của một lớp kế thừa thuộc tính

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
86. Do đó, với một ví dụ, chúng ta có thể tạo các đối tượng “tương tự” thông qua nó

class Person {
  #firstName; // (A)
  constructor(firstName) {
    this.#firstName = firstName; // (B)
  }
  describe() {
    return `Person named ${this.#firstName}`;
  }
  static extractNames(persons) {
    return persons.map(person => person.#firstName);
  }
}
7

Thứ hai, chúng ta có thể lấy tên của lớp đã tạo một thể hiện nhất định

class Person {
  #firstName; // (A)
  constructor(firstName) {
    this.#firstName = firstName; // (B)
  }
  describe() {
    return `Person named ${this.#firstName}`;
  }
  static extractNames(persons) {
    return persons.map(person => person.#firstName);
  }
}
8

29. 3. 5 Đã gửi so với. gọi phương thức trực tiếp (nâng cao)

Trong tiểu mục này, chúng ta tìm hiểu về hai cách gọi phương thức khác nhau.

  • Các cuộc gọi phương thức đã gửi
  • Gọi phương thức trực tiếp

Hiểu cả hai sẽ cho chúng ta những hiểu biết quan trọng về cách thức hoạt động của các phương pháp

Chúng ta cũng sẽ cần đến cách thứ hai ở phần sau của chương này. Nó sẽ cho phép chúng tôi mượn các phương pháp hữu ích từ

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
87

29. 3. 5. 1 Các lệnh gọi phương thức đã gửi

Hãy kiểm tra cách gọi phương thức hoạt động với các lớp. Chúng tôi đang xem lại

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
15 từ trước đó

class Person {
  #firstName; // (A)
  constructor(firstName) {
    this.#firstName = firstName; // (B)
  }
  describe() {
    return `Person named ${this.#firstName}`;
  }
  static extractNames(persons) {
    return persons.map(person => person.#firstName);
  }
}
9

Quả sung. 14 có sơ đồ với chuỗi nguyên mẫu của

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
15

Hình 14. Chuỗi nguyên mẫu của
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
15 bắt đầu với
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
15 và tiếp tục với
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
63

Các cuộc gọi phương thức bình thường được gửi đi - cuộc gọi phương thức

xảy ra trong hai bước

  • Gửi đi. JavaScript đi qua chuỗi nguyên mẫu bắt đầu bằng

    class Employee extends Person {
      constructor(firstName, title) {
        super(firstName);
        this.title = title; // (C)
      }
      describe() {
        return super.describe() +
          ` (${this.title})`;
      }
    }
    
    const jane = new Employee('Jane', 'CTO');
    assert.equal(
      jane.title,
      'CTO'
    );
    assert.equal(
      jane.describe(),
      'Person named Jane (CTO)'
    );
    15 để tìm đối tượng đầu tiên có thuộc tính riêng với khóa
    class Employee extends Person {
      constructor(firstName, title) {
        super(firstName);
        this.title = title; // (C)
      }
      describe() {
        return super.describe() +
          ` (${this.title})`;
      }
    }
    
    const jane = new Employee('Jane', 'CTO');
    assert.equal(
      jane.title,
      'CTO'
    );
    assert.equal(
      jane.describe(),
      'Person named Jane (CTO)'
    );
    94. Đầu tiên, nó xem xét
    class Employee extends Person {
      constructor(firstName, title) {
        super(firstName);
        this.title = title; // (C)
      }
      describe() {
        return super.describe() +
          ` (${this.title})`;
      }
    }
    
    const jane = new Employee('Jane', 'CTO');
    assert.equal(
      jane.title,
      'CTO'
    );
    assert.equal(
      jane.describe(),
      'Person named Jane (CTO)'
    );
    15 và không tìm thấy tài sản riêng
    class Employee extends Person {
      constructor(firstName, title) {
        super(firstName);
        this.title = title; // (C)
      }
      describe() {
        return super.describe() +
          ` (${this.title})`;
      }
    }
    
    const jane = new Employee('Jane', 'CTO');
    assert.equal(
      jane.title,
      'CTO'
    );
    assert.equal(
      jane.describe(),
      'Person named Jane (CTO)'
    );
    96. Nó tiếp tục với nguyên mẫu của
    class Employee extends Person {
      constructor(firstName, title) {
        super(firstName);
        this.title = title; // (C)
      }
      describe() {
        return super.describe() +
          ` (${this.title})`;
      }
    }
    
    const jane = new Employee('Jane', 'CTO');
    assert.equal(
      jane.title,
      'CTO'
    );
    assert.equal(
      jane.describe(),
      'Person named Jane (CTO)'
    );
    15,
    class Employee extends Person {
      constructor(firstName, title) {
        super(firstName);
        this.title = title; // (C)
      }
      describe() {
        return super.describe() +
          ` (${this.title})`;
      }
    }
    
    const jane = new Employee('Jane', 'CTO');
    assert.equal(
      jane.title,
      'CTO'
    );
    assert.equal(
      jane.describe(),
      'Person named Jane (CTO)'
    );
    63 và tìm một thuộc tính riêng của
    class Employee extends Person {
      constructor(firstName, title) {
        super(firstName);
        this.title = title; // (C)
      }
      describe() {
        return super.describe() +
          ` (${this.title})`;
      }
    }
    
    const jane = new Employee('Jane', 'CTO');
    assert.equal(
      jane.title,
      'CTO'
    );
    assert.equal(
      jane.describe(),
      'Person named Jane (CTO)'
    );
    99 có giá trị mà nó trả về

  • Lời kêu gọi. Gọi phương thức một giá trị khác với gọi hàm một giá trị ở chỗ nó không chỉ gọi những gì xuất hiện trước dấu ngoặc đơn với các đối số bên trong dấu ngoặc đơn mà còn đặt

    class Employee extends Person {
      constructor(firstName, title) {
        super(firstName);
        this.title = title; // (C)
      }
      describe() {
        return super.describe() +
          ` (${this.title})`;
      }
    }
    
    const jane = new Employee('Jane', 'CTO');
    assert.equal(
      jane.title,
      'CTO'
    );
    assert.equal(
      jane.describe(),
      'Person named Jane (CTO)'
    );
    22 cho người nhận lệnh gọi phương thức (trong trường hợp này là
    class Employee extends Person {
      constructor(firstName, title) {
        super(firstName);
        this.title = title; // (C)
      }
      describe() {
        return super.describe() +
          ` (${this.title})`;
      }
    }
    
    const jane = new Employee('Jane', 'CTO');
    assert.equal(
      jane.title,
      'CTO'
    );
    assert.equal(
      jane.describe(),
      'Person named Jane (CTO)'
    );
    15)

Cách tự động tìm kiếm một phương thức và gọi nó được gọi là công văn động

29. 3. 5. 2 Các cuộc gọi phương thức trực tiếp

Chúng tôi cũng có thể thực hiện cuộc gọi phương thức trực tiếp mà không cần gửi

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
00

Lần này, chúng tôi trực tiếp trỏ đến phương thức thông qua

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
66 và không tìm kiếm nó trong chuỗi nguyên mẫu. Chúng tôi cũng chỉ định khác nhau cho
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
22 – thông qua
class Person {
  #firstName; // (A)
  constructor(firstName) {
    this.#firstName = firstName; // (B)
  }
  describe() {
    return `Person named ${this.#firstName}`;
  }
  static extractNames(persons) {
    return persons.map(person => person.#firstName);
  }
}
04

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
22 luôn trỏ đến ví dụ

Bất kể phương thức nằm ở đâu trong chuỗi nguyên mẫu của một thể hiện, thì

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
22 luôn trỏ đến thể hiện đó (phần đầu của chuỗi nguyên mẫu). Điều đó cho phép
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
26 truy cập
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
13 trong ví dụ

Khi nào các cuộc gọi phương thức trực tiếp hữu ích?

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
01

29. 3. 6 Lớp phát triển từ chức năng thông thường (nâng cao)

Trước ECMAScript 6, JavaScript không có lớp. Thay vào đó, các hàm thông thường được sử dụng làm hàm tạo

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
02

Các lớp cung cấp cú pháp tốt hơn cho phương pháp này

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
03

Phân lớp đặc biệt phức tạp với các hàm xây dựng. Các lớp học cũng cung cấp các lợi ích vượt xa cú pháp thuận tiện hơn

  • Các hàm xây dựng tích hợp như
    class Employee extends Person {
      constructor(firstName, title) {
        super(firstName);
        this.title = title; // (C)
      }
      describe() {
        return super.describe() +
          ` (${this.title})`;
      }
    }
    
    const jane = new Employee('Jane', 'CTO');
    assert.equal(
      jane.title,
      'CTO'
    );
    assert.equal(
      jane.describe(),
      'Person named Jane (CTO)'
    );
    56 có thể được phân lớp
  • Chúng tôi có thể truy cập các thuộc tính bị ghi đè thông qua
    class Person {
      #firstName; // (A)
      constructor(firstName) {
        this.#firstName = firstName; // (B)
      }
      describe() {
        return `Person named ${this.#firstName}`;
      }
      static extractNames(persons) {
        return persons.map(person => person.#firstName);
      }
    }
    10
  • Các lớp không thể được gọi theo hàm
  • Các phương thức không thể được gọi là
    class Employee extends Person {
      constructor(firstName, title) {
        super(firstName);
        this.title = title; // (C)
      }
      describe() {
        return super.describe() +
          ` (${this.title})`;
      }
    }
    
    const jane = new Employee('Jane', 'CTO');
    assert.equal(
      jane.title,
      'CTO'
    );
    assert.equal(
      jane.describe(),
      'Person named Jane (CTO)'
    );
    81 và không có thuộc tính
    class Employee extends Person {
      constructor(firstName, title) {
        super(firstName);
        this.title = title; // (C)
      }
      describe() {
        return super.describe() +
          ` (${this.title})`;
      }
    }
    
    const jane = new Employee('Jane', 'CTO');
    assert.equal(
      jane.title,
      'CTO'
    );
    assert.equal(
      jane.describe(),
      'Person named Jane (CTO)'
    );
    70
  • Hỗ trợ dữ liệu phiên bản riêng tư
  • Và nhiều hơn nữa

Các lớp tương thích với các hàm xây dựng đến mức chúng thậm chí có thể mở rộng chúng

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
04

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
37 và phân lớp được giải thích sau trong chương này

29. 3. 6. Lớp 1 A là hàm tạo

Điều này đưa chúng ta đến một cái nhìn sâu sắc thú vị. Một mặt,

class Person {
  #firstName; // (A)
  constructor(firstName) {
    this.#firstName = firstName; // (B)
  }
  describe() {
    return `Person named ${this.#firstName}`;
  }
  static extractNames(persons) {
    return persons.map(person => person.#firstName);
  }
}
14 đề cập đến hàm tạo của nó thông qua
class Person {
  #firstName; // (A)
  constructor(firstName) {
    this.#firstName = firstName; // (B)
  }
  describe() {
    return `Person named ${this.#firstName}`;
  }
  static extractNames(persons) {
    return persons.map(person => person.#firstName);
  }
}
15

Mặt khác, lớp là hàm tạo (một hàm)

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
05

Hàm tạo (hàm) so với. các lớp học

Do chúng giống nhau như thế nào, tôi sử dụng thuật ngữ hàm tạo (hàm) và lớp thay thế cho nhau

29. 4 Thành viên nguyên mẫu của các lớp

29. 4. 1 Các phương thức và trình truy cập nguyên mẫu công khai

Tất cả các thành viên trong phần thân của khai báo lớp sau tạo các thuộc tính của

class Person {
  #firstName; // (A)
  constructor(firstName) {
    this.#firstName = firstName; // (B)
  }
  describe() {
    return `Person named ${this.#firstName}`;
  }
  static extractNames(persons) {
    return persons.map(person => person.#firstName);
  }
}
16

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
06

29. 4. 1. 1 Tất cả các loại phương thức và trình truy cập nguyên mẫu công khai (nâng cao)

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
07

Các khóa được trích dẫn và tính toán cũng có thể được sử dụng theo nghĩa đen của đối tượng

Thông tin thêm về bộ truy cập (được xác định thông qua getters và/hoặc setters), trình tạo, phương thức không đồng bộ và phương thức tạo không đồng bộ

29. 4. 2 Các phương thức và trình truy cập riêng tư [ES2022]

Các phương thức riêng tư (và trình truy cập) là sự kết hợp thú vị giữa các thành viên nguyên mẫu và thành viên cá thể

Một mặt, các phương thức riêng tư được lưu trữ trong các vị trí trong phiên bản (dòng A)

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
08

Tại sao chúng không được lưu trữ trong các đối tượng

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
70?

Mặt khác, các phương thức riêng tư được chia sẻ giữa các phiên bản - như các phương thức công khai nguyên mẫu

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
09

Do đó và do cú pháp của chúng tương tự như các phương thức công khai nguyên mẫu, chúng được đề cập ở đây

Đoạn mã sau minh họa cách hoạt động của các phương thức và trình truy cập riêng tư

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
60

29. 4. 2. 1 Tất cả các loại phương thức và trình truy cập riêng tư (nâng cao)

Với các vị trí riêng tư, các khóa luôn là số nhận dạng

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
61

Thông tin thêm về bộ truy cập (được xác định thông qua getters và/hoặc setters), trình tạo, phương thức không đồng bộ và phương thức tạo không đồng bộ

29. 5 Thành viên thực thể của các lớp [ES2022]

29. 5. Trường công khai 1 Instance

Các thể hiện của lớp sau có hai thuộc tính thể hiện (được tạo ở dòng A và dòng B)

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
62

Nếu chúng ta tạo một thuộc tính thể hiện bên trong hàm tạo (dòng B), chúng ta không cần phải “khai báo” nó ở nơi khác. Như chúng ta đã thấy, điều đó khác với các trường riêng chẳng hạn

Lưu ý rằng các thuộc tính thể hiện tương đối phổ biến trong JavaScript; . g. , Java, trong đó hầu hết trạng thái cá thể là riêng tư

29. 5. 1. 1 Các trường công khai của phiên bản với các khóa được trích dẫn và tính toán (nâng cao)

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
63

29. 5. 1. 2 Giá trị của
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
22 trong các trường công khai thể hiện là bao nhiêu?

Trong trình khởi tạo của trường công khai phiên bản,

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
22 đề cập đến phiên bản mới được tạo

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
64

29. 5. 1. 3 When are instance public fields executed? (advanced)

The execution of instance public fields roughly follows these two rules

  • Trong các lớp cơ sở (các lớp không có siêu lớp), các trường công khai của cá thể được thực thi ngay trước hàm tạo
  • Trong các lớp dẫn xuất (các lớp có siêu lớp)
    • Lớp cha thiết lập các vị trí phiên bản của nó khi
      class Person {
        #firstName; // (A)
        constructor(firstName) {
          this.#firstName = firstName; // (B)
        }
        describe() {
          return `Person named ${this.#firstName}`;
        }
        static extractNames(persons) {
          return persons.map(person => person.#firstName);
        }
      }
      20 được gọi
    • Các trường công khai của cá thể được thực thi ngay sau
      class Person {
        #firstName; // (A)
        constructor(firstName) {
          this.#firstName = firstName; // (B)
        }
        describe() {
          return `Person named ${this.#firstName}`;
        }
        static extractNames(persons) {
          return persons.map(person => person.#firstName);
        }
      }
      20

Ví dụ sau minh họa các quy tắc này

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
65

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
37 và phân lớp được giải thích sau trong chương này

29. 5. Trường riêng 2 Instance

Lớp sau chứa hai trường riêng (dòng A và dòng B)

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
66

Lưu ý rằng chúng ta chỉ có thể sử dụng

class Person {
  #firstName; // (A)
  constructor(firstName) {
    this.#firstName = firstName; // (B)
  }
  describe() {
    return `Person named ${this.#firstName}`;
  }
  static extractNames(persons) {
    return persons.map(person => person.#firstName);
  }
}
23 trong dòng C nếu chúng ta khai báo nó trong phần thân của lớp

29. 5. 3 Dữ liệu phiên bản riêng tư trước ES2022 (nâng cao)

In this section, we look at two techniques for keeping instance data private. Because they don’t rely on classes, we can also use them for objects that were created in other ways – e. g. , via object literals

29. 5. 3. 1 Before ES6. private members via naming conventions

The first technique makes a property private by prefixing its name with an underscore. This doesn’t protect the property in any way; it merely signals to the outside. “You don’t need to know about this property. ”

In the following code, the properties

class Person {
  #firstName; // (A)
  constructor(firstName) {
    this.#firstName = firstName; // (B)
  }
  describe() {
    return `Person named ${this.#firstName}`;
  }
  static extractNames(persons) {
    return persons.map(person => person.#firstName);
  }
}
24 and
class Person {
  #firstName; // (A)
  constructor(firstName) {
    this.#firstName = firstName; // (B)
  }
  describe() {
    return `Person named ${this.#firstName}`;
  }
  static extractNames(persons) {
    return persons.map(person => person.#firstName);
  }
}
25 are private

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
67

With this technique, we don’t get any protection and private names can clash. On the plus side, it is easy to use

Private methods work similarly. They are normal methods whose names start with underscores

29. 5. 3. 2 ES6 and later. private instance data via WeakMaps

We can also manage private instance data via WeakMaps

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
68

How exactly that works is explained in the chapter on WeakMaps

This technique offers us considerable protection from outside access and there can’t be any name clashes. But it is also more complicated to use

We control the visibility of the pseudo-property

class Person {
  #firstName; // (A)
  constructor(firstName) {
    this.#firstName = firstName; // (B)
  }
  describe() {
    return `Person named ${this.#firstName}`;
  }
  static extractNames(persons) {
    return persons.map(person => person.#firstName);
  }
}
26 by controlling who has access to it – for example. If the variable exists inside a module and isn’t exported, everyone inside the module and no one outside the module can access it. In other words. The scope of privacy isn’t the class in this case, it’s the module. We could narrow the scope, though

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
69

This technique doesn’t really support private methods. But module-local functions that have access to

class Person {
  #firstName; // (A)
  constructor(firstName) {
    this.#firstName = firstName; // (B)
  }
  describe() {
    return `Person named ${this.#firstName}`;
  }
  static extractNames(persons) {
    return persons.map(person => person.#firstName);
  }
}
26 are the next best thing

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
30

Note that

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
22 becomes the explicit function parameter
class Person {
  #firstName; // (A)
  constructor(firstName) {
    this.#firstName = firstName; // (B)
  }
  describe() {
    return `Person named ${this.#firstName}`;
  }
  static extractNames(persons) {
    return persons.map(person => person.#firstName);
  }
}
29 (line A)

29. 5. 4 Simulating protected visibility and friend visibility via WeakMaps (advanced)

As previously discussed, instance private fields are only visible inside their classes and not even in subclasses. Thus, there is no built-in way to get

  • Protected visibility. A class and all of its subclasses can access a piece instance data
  • Friend visibility. A class and its “friends” (designated functions, objects, or classes) can access a piece of instance data

In the previous subsection, we simulated “module visibility” (everyone inside a module has access to a piece of instance data) via WeakMaps. Therefore

  • If we put a class and its subclasses into the same module, we get protected visibility
  • If we put a class and its friends into the same module, we get friend visibility

The next example demonstrates protected visibility

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
31

Subclassing via

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
37 is explained later in this chapter

29. 6 Static members of classes

29. 6. 1 Static public methods and accessors

All members in the body of the following class declaration create so-called static properties – properties of

class Person {
  #firstName; // (A)
  constructor(firstName) {
    this.#firstName = firstName; // (B)
  }
  describe() {
    return `Person named ${this.#firstName}`;
  }
  static extractNames(persons) {
    return persons.map(person => person.#firstName);
  }
}
31 itself

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
32

29. 6. 1. 1 All kinds of static public methods and accessors (advanced)

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
33

Các khóa được trích dẫn và tính toán cũng có thể được sử dụng theo nghĩa đen của đối tượng

Thông tin thêm về bộ truy cập (được xác định thông qua getters và/hoặc setters), trình tạo, phương thức không đồng bộ và phương thức tạo không đồng bộ

29. 6. 2 Static public fields [ES2022]

The following code demonstrates static public fields.

class Person {
  #firstName; // (A)
  constructor(firstName) {
    this.#firstName = firstName; // (B)
  }
  describe() {
    return `Person named ${this.#firstName}`;
  }
  static extractNames(persons) {
    return persons.map(person => person.#firstName);
  }
}
32 has three of them

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
34

29. 6. 3 Static private methods, accessors, and fields [ES2022]

The following class has two static private slots (line A and line B)

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
35

This is a complete list of all kinds of static private slots

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
36

29. 6. 4 Static initialization blocks in classes [ES2022]

To set up instance data via classes, we have two constructs

  • Fields, to create and optionally initialize instance data
  • Constructors, blocks of code that are executed every time a new instance is created

For static data, we have

  • Static fields
  • Static blocks that are executed when a class is created

The following code demonstrates static blocks (line A)

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
37

We could also execute the code inside the static block after the class (at the top level). However, using a static block has two benefits

  • All class-related code is inside the class
  • The code in a static block has access to private slots
29. 6. 4. 1 Rules for static initialization blocks

The rules for how static initialization blocks work, are relatively simple

  • There can be more than one static block per class
  • The execution of static blocks is interleaved with the execution of static field initializers
  • The static members of a superclass are executed before the static members of a subclass

The following code demonstrates these rules

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
38

Subclassing via

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
37 is explained later in this chapter

29. 6. 5 Pitfall. Using
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
22 to access static private fields

In static public members, we can access static public slots via

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
22. Alas, we should not use it to access static private slots

29. 6. 5. 1 
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
22 and static public fields

Consider the following code

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
39

Subclassing via

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
37 is explained later in this chapter

Static public fields are properties. If we make the method call

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
50

then

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
22 points to
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
43 and everything works as expected. We can also invoke
class Person {
  #firstName; // (A)
  constructor(firstName) {
    this.#firstName = firstName; // (B)
  }
  describe() {
    return `Person named ${this.#firstName}`;
  }
  static extractNames(persons) {
    return persons.map(person => person.#firstName);
  }
}
40 via the subclass

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
51

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
45 inherits
class Person {
  #firstName; // (A)
  constructor(firstName) {
    this.#firstName = firstName; // (B)
  }
  describe() {
    return `Person named ${this.#firstName}`;
  }
  static extractNames(persons) {
    return persons.map(person => person.#firstName);
  }
}
40 from its prototype
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
43.
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
22 points to
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
45 and things continue to work, because
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
45 also inherits the property
class Person {
  #firstName; // (A)
  constructor(firstName) {
    this.#firstName = firstName; // (B)
  }
  describe() {
    return `Person named ${this.#firstName}`;
  }
  static extractNames(persons) {
    return persons.map(person => person.#firstName);
  }
}
47

As an aside, if we assigned to

class Person {
  #firstName; // (A)
  constructor(firstName) {
    this.#firstName = firstName; // (B)
  }
  describe() {
    return `Person named ${this.#firstName}`;
  }
  static extractNames(persons) {
    return persons.map(person => person.#firstName);
  }
}
48 in
class Person {
  #firstName; // (A)
  constructor(firstName) {
    this.#firstName = firstName; // (B)
  }
  describe() {
    return `Person named ${this.#firstName}`;
  }
  static extractNames(persons) {
    return persons.map(person => person.#firstName);
  }
}
49 and invoked it via
class Person {
  #firstName; // (A)
  constructor(firstName) {
    this.#firstName = firstName; // (B)
  }
  describe() {
    return `Person named ${this.#firstName}`;
  }
  static extractNames(persons) {
    return persons.map(person => person.#firstName);
  }
}
50, then we would create a new own poperty of
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
45 that (non-destructively) overrides the property inherited from
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
43

29. 6. 5. 2 
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
22 and static private fields

Consider the following code

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
52

Invoking

class Person {
  #firstName; // (A)
  constructor(firstName) {
    this.#firstName = firstName; // (B)
  }
  describe() {
    return `Person named ${this.#firstName}`;
  }
  static extractNames(persons) {
    return persons.map(person => person.#firstName);
  }
}
54 via
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
43 works, because
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
22 points to
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
43

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
53

However, invoking

class Person {
  #firstName; // (A)
  constructor(firstName) {
    this.#firstName = firstName; // (B)
  }
  describe() {
    return `Person named ${this.#firstName}`;
  }
  static extractNames(persons) {
    return persons.map(person => person.#firstName);
  }
}
54 via
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
45 does not work, because
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
22 now points to
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
45 and
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
45 has no static private field
class Person {
  #firstName; // (A)
  constructor(firstName) {
    this.#firstName = firstName; // (B)
  }
  describe() {
    return `Person named ${this.#firstName}`;
  }
  static extractNames(persons) {
    return persons.map(person => person.#firstName);
  }
}
63 (private slots in prototype chains are not inherited)

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
54

The workaround is to accesss

class Person {
  #firstName; // (A)
  constructor(firstName) {
    this.#firstName = firstName; // (B)
  }
  describe() {
    return `Person named ${this.#firstName}`;
  }
  static extractNames(persons) {
    return persons.map(person => person.#firstName);
  }
}
63 directly, via
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
43

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
55

With static private methods, we are facing the same issue

29. 6. 6 All members (static, prototype, instance) can access all private members

Mọi thành viên trong một lớp có thể truy cập tất cả các thành viên khác trong lớp đó - cả công khai và riêng tư

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
56

In contrast, no one outside can access the private members

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
57

29. 6. 7 Static private methods and data before ES2022

Đoạn mã sau chỉ hoạt động trong ES2022 – do mỗi dòng có ký hiệu băm (

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
24) trong đó

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
58

Since private slots only exist once per class, we can move

class Person {
  #firstName; // (A)
  constructor(firstName) {
    this.#firstName = firstName; // (B)
  }
  describe() {
    return `Person named ${this.#firstName}`;
  }
  static extractNames(persons) {
    return persons.map(person => person.#firstName);
  }
}
67 and
class Person {
  #firstName; // (A)
  constructor(firstName) {
    this.#firstName = firstName; // (B)
  }
  describe() {
    return `Person named ${this.#firstName}`;
  }
  static extractNames(persons) {
    return persons.map(person => person.#firstName);
  }
}
68 to the scope surrounding the class and use a module to hide them from the world outside the module

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
59

29. 6. 8 Phương pháp nhà máy tĩnh

Đôi khi có nhiều cách để khởi tạo một lớp. Sau đó, chúng ta có thể triển khai các phương thức tĩnh của nhà máy, chẳng hạn như

class Person {
  #firstName; // (A)
  constructor(firstName) {
    this.#firstName = firstName; // (B)
  }
  describe() {
    return `Person named ${this.#firstName}`;
  }
  static extractNames(persons) {
    return persons.map(person => person.#firstName);
  }
}
69

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
60

Tôi thích cách mô tả các phương thức nhà máy tĩnh.

class Person {
  #firstName; // (A)
  constructor(firstName) {
    this.#firstName = firstName; // (B)
  }
  describe() {
    return `Person named ${this.#firstName}`;
  }
  static extractNames(persons) {
    return persons.map(person => person.#firstName);
  }
}
70 describes how an instance is created. JavaScript’s standard library also has such factory methods – for example

  • class Person {
      #firstName; // (A)
      constructor(firstName) {
        this.#firstName = firstName; // (B)
      }
      describe() {
        return `Person named ${this.#firstName}`;
      }
      static extractNames(persons) {
        return persons.map(person => person.#firstName);
      }
    }
    71
  • class Person {
      #firstName; // (A)
      constructor(firstName) {
        this.#firstName = firstName; // (B)
      }
      describe() {
        return `Person named ${this.#firstName}`;
      }
      static extractNames(persons) {
        return persons.map(person => person.#firstName);
      }
    }
    72

I prefer to either have no static factory methods or only static factory methods. Things to consider in the latter case

  • One factory method will probably directly call the constructor (but have a descriptive name)
  • We need to find a way to prevent the constructor being called from outside

In the following code, we use a secret token (line A) to prevent the constructor being called from outside the current module

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
61

29. 7 Subclassing

Classes can also extend existing classes. For example, the following class

class Person {
  #firstName; // (A)
  constructor(firstName) {
    this.#firstName = firstName; // (B)
  }
  describe() {
    return `Person named ${this.#firstName}`;
  }
  static extractNames(persons) {
    return persons.map(person => person.#firstName);
  }
}
73 extends
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
20

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
62

Terminology related to extending

  • Another word for extending is subclassing
  • class Employee extends Person {
      constructor(firstName, title) {
        super(firstName);
        this.title = title; // (C)
      }
      describe() {
        return super.describe() +
          ` (${this.title})`;
      }
    }
    
    const jane = new Employee('Jane', 'CTO');
    assert.equal(
      jane.title,
      'CTO'
    );
    assert.equal(
      jane.describe(),
      'Person named Jane (CTO)'
    );
    20 is the superclass of
    class Person {
      #firstName; // (A)
      constructor(firstName) {
        this.#firstName = firstName; // (B)
      }
      describe() {
        return `Person named ${this.#firstName}`;
      }
      static extractNames(persons) {
        return persons.map(person => person.#firstName);
      }
    }
    73
  • class Person {
      #firstName; // (A)
      constructor(firstName) {
        this.#firstName = firstName; // (B)
      }
      describe() {
        return `Person named ${this.#firstName}`;
      }
      static extractNames(persons) {
        return persons.map(person => person.#firstName);
      }
    }
    73 is the subclass of
    class Employee extends Person {
      constructor(firstName, title) {
        super(firstName);
        this.title = title; // (C)
      }
      describe() {
        return super.describe() +
          ` (${this.title})`;
      }
    }
    
    const jane = new Employee('Jane', 'CTO');
    assert.equal(
      jane.title,
      'CTO'
    );
    assert.equal(
      jane.describe(),
      'Person named Jane (CTO)'
    );
    20
  • A base class is a class that has no superclasses
  • A derived class is a class that has a superclass

Inside the

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
21 of a derived class, we must call the super-constructor via
class Person {
  #firstName; // (A)
  constructor(firstName) {
    this.#firstName = firstName; // (B)
  }
  describe() {
    return `Person named ${this.#firstName}`;
  }
  static extractNames(persons) {
    return persons.map(person => person.#firstName);
  }
}
20 before we can access
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
22. Why is that?

Let’s consider a chain of classes

  • Base class
    class Person {
      #firstName; // (A)
      constructor(firstName) {
        this.#firstName = firstName; // (B)
      }
      describe() {
        return `Person named ${this.#firstName}`;
      }
      static extractNames(persons) {
        return persons.map(person => person.#firstName);
      }
    }
    82
  • Class
    class Person {
      #firstName; // (A)
      constructor(firstName) {
        this.#firstName = firstName; // (B)
      }
      describe() {
        return `Person named ${this.#firstName}`;
      }
      static extractNames(persons) {
        return persons.map(person => person.#firstName);
      }
    }
    83 extends
    class Person {
      #firstName; // (A)
      constructor(firstName) {
        this.#firstName = firstName; // (B)
      }
      describe() {
        return `Person named ${this.#firstName}`;
      }
      static extractNames(persons) {
        return persons.map(person => person.#firstName);
      }
    }
    82
  • Class
    class Employee extends Person {
      constructor(firstName, title) {
        super(firstName);
        this.title = title; // (C)
      }
      describe() {
        return super.describe() +
          ` (${this.title})`;
      }
    }
    
    const jane = new Employee('Jane', 'CTO');
    assert.equal(
      jane.title,
      'CTO'
    );
    assert.equal(
      jane.describe(),
      'Person named Jane (CTO)'
    );
    57 extends
    class Person {
      #firstName; // (A)
      constructor(firstName) {
        this.#firstName = firstName; // (B)
      }
      describe() {
        return `Person named ${this.#firstName}`;
      }
      static extractNames(persons) {
        return persons.map(person => person.#firstName);
      }
    }
    83

If we invoke

class Person {
  #firstName; // (A)
  constructor(firstName) {
    this.#firstName = firstName; // (B)
  }
  describe() {
    return `Person named ${this.#firstName}`;
  }
  static extractNames(persons) {
    return persons.map(person => person.#firstName);
  }
}
87,
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
57’s constructor super-calls
class Person {
  #firstName; // (A)
  constructor(firstName) {
    this.#firstName = firstName; // (B)
  }
  describe() {
    return `Person named ${this.#firstName}`;
  }
  static extractNames(persons) {
    return persons.map(person => person.#firstName);
  }
}
83’s constructor which super-calls
class Person {
  #firstName; // (A)
  constructor(firstName) {
    this.#firstName = firstName; // (B)
  }
  describe() {
    return `Person named ${this.#firstName}`;
  }
  static extractNames(persons) {
    return persons.map(person => person.#firstName);
  }
}
82’s constructor. Instances are always created in base classes, before the constructors of subclasses add their slots. Therefore, the instance doesn’t exist before we call
class Person {
  #firstName; // (A)
  constructor(firstName) {
    this.#firstName = firstName; // (B)
  }
  describe() {
    return `Person named ${this.#firstName}`;
  }
  static extractNames(persons) {
    return persons.map(person => person.#firstName);
  }
}
20 and we can’t access it via
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
22, yet

Note that static public slots are inherited. For example,

class Person {
  #firstName; // (A)
  constructor(firstName) {
    this.#firstName = firstName; // (B)
  }
  describe() {
    return `Person named ${this.#firstName}`;
  }
  static extractNames(persons) {
    return persons.map(person => person.#firstName);
  }
}
73 inherits the static method
class Person {
  #firstName; // (A)
  constructor(firstName) {
    this.#firstName = firstName; // (B)
  }
  describe() {
    return `Person named ${this.#firstName}`;
  }
  static extractNames(persons) {
    return persons.map(person => person.#firstName);
  }
}
94

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
63

  Exercise. Subclassing

class Person {
  #firstName; // (A)
  constructor(firstName) {
    this.#firstName = firstName; // (B)
  }
  describe() {
    return `Person named ${this.#firstName}`;
  }
  static extractNames(persons) {
    return persons.map(person => person.#firstName);
  }
}
95

29. 7. 1 The internals of subclassing (advanced)

Figure 15. These are the objects that make up class
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
20 and its subclass,
class Person {
  #firstName; // (A)
  constructor(firstName) {
    this.#firstName = firstName; // (B)
  }
  describe() {
    return `Person named ${this.#firstName}`;
  }
  static extractNames(persons) {
    return persons.map(person => person.#firstName);
  }
}
73. The left column is about classes. The right column is about the
class Person {
  #firstName; // (A)
  constructor(firstName) {
    this.#firstName = firstName; // (B)
  }
  describe() {
    return `Person named ${this.#firstName}`;
  }
  static extractNames(persons) {
    return persons.map(person => person.#firstName);
  }
}
73 instance
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
15 and its prototype chain

The classes

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
20 and
class Person {
  #firstName; // (A)
  constructor(firstName) {
    this.#firstName = firstName; // (B)
  }
  describe() {
    return `Person named ${this.#firstName}`;
  }
  static extractNames(persons) {
    return persons.map(person => person.#firstName);
  }
}
73 from the previous section are made up of several objects (fig.  15). One key insight for understanding how these objects are related is that there are two prototype chains

  • The instance prototype chain, on the right
  • The class prototype chain, on the left
29. 7. 1. 1 The instance prototype chain (right column)

Chuỗi nguyên mẫu phiên bản bắt đầu với

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
15 và tiếp tục với
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
003 và
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
63. In principle, the prototype chain ends at this point, but we get one more object.
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
87. This prototype provides services to virtually all objects, which is why it is included here, too

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
64

29. 7. 1. 2 Chuỗi nguyên mẫu lớp (cột bên trái)

Trong chuỗi nguyên mẫu lớp,

class Person {
  #firstName; // (A)
  constructor(firstName) {
    this.#firstName = firstName; // (B)
  }
  describe() {
    return `Person named ${this.#firstName}`;
  }
  static extractNames(persons) {
    return persons.map(person => person.#firstName);
  }
}
73 đến trước,
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
20 tiếp theo. Sau đó, chuỗi tiếp tục với
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
008, chỉ ở đó vì
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
20 là một chức năng và các chức năng này cần các dịch vụ của
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
008

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
65

29. 7. 2 
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
32 và phân lớp (nâng cao)

Chúng tôi vẫn chưa biết cách thức hoạt động của

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
32. Làm cách nào để
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
32 xác định xem giá trị
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
014 có phải là một thể hiện của lớp
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
57 hay không (nó có thể là một thể hiện trực tiếp của
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
57 hoặc một thể hiện trực tiếp của một lớp con của
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
57)? . Đó là, hai biểu thức sau đây là tương đương

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
66

Nếu chúng ta quay trở lại hình. 15, chúng tôi có thể xác nhận rằng chuỗi nguyên mẫu đưa chúng tôi đến các câu trả lời đúng sau đây

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
67

Lưu ý rằng

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
32 luôn trả về
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
021 nếu phía bên tay của nó là một giá trị nguyên thủy

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
68

29. 7. 3 Không phải tất cả các đối tượng đều là phiên bản của
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
79 (nâng cao)

Một đối tượng (giá trị không nguyên thủy) chỉ là một thể hiện của

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
79 nếu
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
87 nằm trong chuỗi nguyên mẫu của nó (xem tiểu mục trước). Hầu như tất cả các đối tượng là thể hiện của
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
79 – ví dụ

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
69

Trong ví dụ tiếp theo,

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
026 và
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
027 đều là đối tượng (dòng A và dòng C), nhưng chúng không phải là thể hiện của
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
79 (dòng B và dòng D).
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
87 không có trong chuỗi nguyên mẫu của họ vì họ không có bất kỳ nguyên mẫu nào

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
70

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
87 là đối tượng kết thúc hầu hết các chuỗi nguyên mẫu. Nguyên mẫu của nó là
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
031, có nghĩa là nó cũng không phải là phiên bản của
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
79

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
71

29. 7. 4 Chuỗi nguyên mẫu của các đối tượng tích hợp (nâng cao)

Tiếp theo, chúng ta sẽ sử dụng kiến ​​thức về phân lớp để hiểu chuỗi nguyên mẫu của một vài đối tượng tích hợp. Chức năng công cụ sau đây

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
033 giúp chúng tôi khám phá

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
72

Chúng tôi đã trích xuất phương thức

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
034 của
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
79 và gán nó cho
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
036

29. 7. 4. 1 Chuỗi nguyên mẫu của
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
037

Hãy bắt đầu bằng cách kiểm tra các đối tượng đơn giản

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
73

Hình 16. Chuỗi nguyên mẫu của một đối tượng được tạo thông qua một đối tượng theo nghĩa đen bắt đầu bằng đối tượng đó, tiếp tục với
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
87 và kết thúc bằng
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
031

Quả sung. 16 hiển thị sơ đồ cho chuỗi nguyên mẫu này. Chúng ta có thể thấy rằng

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
037 thực sự là một thể hiện của
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
79 –
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
87 nằm trong chuỗi nguyên mẫu của nó

29. 7. 4. 2 Chuỗi nguyên mẫu của
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
043

Chuỗi nguyên mẫu của một Mảng trông như thế nào?

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
74

Hình 17. Chuỗi nguyên mẫu của một Mảng có các thành viên này. thể hiện Mảng,
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
044,
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
87,
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
031

Chuỗi nguyên mẫu này (được hiển thị trong hình. 17) cho chúng ta biết rằng một đối tượng Array là một thể hiện của

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
047 và của
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
79

29. 7. 4. 3 Chuỗi nguyên mẫu của
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
049

Cuối cùng, chuỗi nguyên mẫu của một hàm thông thường cho chúng ta biết rằng tất cả các hàm đều là đối tượng

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
75

29. 7. 4. 4 Chuỗi nguyên mẫu của các lớp dựng sẵn

Nguyên mẫu của một lớp cơ sở là

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
008 có nghĩa là nó là một hàm (một thể hiện của
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
051)

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
76

Nguyên mẫu của lớp dẫn xuất là lớp cha của nó

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
77

Thật thú vị,

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
79,
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
047 và
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
051 đều là các lớp cơ sở

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
78

Tuy nhiên, như chúng ta đã thấy, ngay cả các thể hiện của các lớp cơ sở cũng có

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
87 trong chuỗi nguyên mẫu của chúng vì nó cung cấp các dịch vụ mà tất cả các đối tượng cần

Tại sao lại là các lớp cơ sở

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
047 và
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
051?

Base classes are where instances are actually created. Cả

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
047 và
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
051 đều cần tạo các phiên bản của riêng chúng vì chúng có cái gọi là "khe nội bộ" mà sau này không thể thêm vào các phiên bản được tạo bởi
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
79

29. 7. Lớp 5 Mixin (nâng cao)

Hệ thống lớp của JavaScript chỉ hỗ trợ kế thừa đơn. Tức là mỗi lớp có thể có nhiều nhất một lớp cha. Một cách để giải quyết hạn chế này là thông qua một kỹ thuật gọi là các lớp mixin (viết tắt. hỗn hợp)

Ý tưởng là như sau. Giả sử chúng ta muốn một lớp

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
57 kế thừa từ hai lớp cha
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
062 và
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
063. Đó sẽ là đa kế thừa mà JavaScript không hỗ trợ

Cách giải quyết của chúng tôi là biến

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
062 và
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
063 thành mixin, nhà máy cho các lớp con

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
79

Mỗi hàm trong số hai hàm này trả về một lớp mở rộng một siêu lớp đã cho

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
066. Ta tạo lớp
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
57 như sau

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
00

Bây giờ chúng ta có một lớp

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
57 mở rộng lớp được trả về bởi
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
069 mở rộng lớp được trả về bởi
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
070 mở rộng cho
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
79

29. 7. 5. 1 Ví dụ. một mixin để quản lý thương hiệu

Chúng tôi triển khai một mixin

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
072 có các phương thức trợ giúp để thiết lập và nhận thương hiệu của một đối tượng

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
01

Chúng tôi sử dụng mixin này để triển khai lớp

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
073 có tên

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
02

Đoạn mã sau xác nhận rằng mixin hoạt động

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
03

29. 7. 5. 2 Lợi ích của mixin

Mixins giải phóng chúng ta khỏi những ràng buộc của thừa kế đơn lẻ

  • Cùng một lớp có thể mở rộng một siêu lớp và không hoặc nhiều mixin
  • Nhiều lớp có thể sử dụng cùng một mixin

29. 8 Các phương thức và bộ truy cập của class Employee extends Person { constructor(firstName, title) { super(firstName); this.title = title; // (C) } describe() { return super.describe() + ` (${this.title})`; } } const jane = new Employee('Jane', 'CTO'); assert.equal( jane.title, 'CTO' ); assert.equal( jane.describe(), 'Person named Jane (CTO)' );87 (nâng cao)

Như chúng ta đã thấy ở §29. 7. 3 “Không phải tất cả các đối tượng đều là phiên bản của

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
79”, hầu hết tất cả các đối tượng đều là phiên bản của
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
79. Lớp này cung cấp một số phương thức hữu ích và một trình truy cập vào các thể hiện của nó

  • Định cấu hình cách các đối tượng được chuyển đổi thành các giá trị nguyên thủy (e. g. bởi nhà điều hành
    class Employee extends Person {
      constructor(firstName, title) {
        super(firstName);
        this.title = title; // (C)
      }
      describe() {
        return super.describe() +
          ` (${this.title})`;
      }
    }
    
    const jane = new Employee('Jane', 'CTO');
    assert.equal(
      jane.title,
      'CTO'
    );
    assert.equal(
      jane.describe(),
      'Person named Jane (CTO)'
    );
    077). Các phương thức sau đây có cài đặt mặc định nhưng thường bị ghi đè trong các lớp con hoặc phiên bản
    • class Employee extends Person {
        constructor(firstName, title) {
          super(firstName);
          this.title = title; // (C)
        }
        describe() {
          return super.describe() +
            ` (${this.title})`;
        }
      }
      
      const jane = new Employee('Jane', 'CTO');
      assert.equal(
        jane.title,
        'CTO'
      );
      assert.equal(
        jane.describe(),
        'Person named Jane (CTO)'
      );
      078. Cấu hình cách một đối tượng được chuyển đổi thành một chuỗi
    • class Employee extends Person {
        constructor(firstName, title) {
          super(firstName);
          this.title = title; // (C)
        }
        describe() {
          return super.describe() +
            ` (${this.title})`;
        }
      }
      
      const jane = new Employee('Jane', 'CTO');
      assert.equal(
        jane.title,
        'CTO'
      );
      assert.equal(
        jane.describe(),
        'Person named Jane (CTO)'
      );
      079. Phiên bản của
      class Employee extends Person {
        constructor(firstName, title) {
          super(firstName);
          this.title = title; // (C)
        }
        describe() {
          return super.describe() +
            ` (${this.title})`;
        }
      }
      
      const jane = new Employee('Jane', 'CTO');
      assert.equal(
        jane.title,
        'CTO'
      );
      assert.equal(
        jane.describe(),
        'Person named Jane (CTO)'
      );
      078 có thể được định cấu hình theo nhiều cách khác nhau thông qua các đối số (ngôn ngữ, khu vực, v.v. )
    • class Employee extends Person {
        constructor(firstName, title) {
          super(firstName);
          this.title = title; // (C)
        }
        describe() {
          return super.describe() +
            ` (${this.title})`;
        }
      }
      
      const jane = new Employee('Jane', 'CTO');
      assert.equal(
        jane.title,
        'CTO'
      );
      assert.equal(
        jane.describe(),
        'Person named Jane (CTO)'
      );
      081. Định cấu hình cách đối tượng được chuyển đổi thành giá trị nguyên thủy không phải chuỗi (thường là số)
  • Các phương pháp hữu ích (có cạm bẫy - xem tiểu mục tiếp theo)
  • Tránh các tính năng này (có những lựa chọn thay thế tốt hơn)
    • class Employee extends Person {
        constructor(firstName, title) {
          super(firstName);
          this.title = title; // (C)
        }
        describe() {
          return super.describe() +
            ` (${this.title})`;
        }
      }
      
      const jane = new Employee('Jane', 'CTO');
      assert.equal(
        jane.title,
        'CTO'
      );
      assert.equal(
        jane.describe(),
        'Person named Jane (CTO)'
      );
      74. Nhận và thiết lập nguyên mẫu của máy thu
      • Không nên sử dụng bộ truy cập này. lựa chọn thay thế
        • class Employee extends Person {
            constructor(firstName, title) {
              super(firstName);
              this.title = title; // (C)
            }
            describe() {
              return super.describe() +
                ` (${this.title})`;
            }
          }
          
          const jane = new Employee('Jane', 'CTO');
          assert.equal(
            jane.title,
            'CTO'
          );
          assert.equal(
            jane.describe(),
            'Person named Jane (CTO)'
          );
          083
        • class Employee extends Person {
            constructor(firstName, title) {
              super(firstName);
              this.title = title; // (C)
            }
            describe() {
              return super.describe() +
                ` (${this.title})`;
            }
          }
          
          const jane = new Employee('Jane', 'CTO');
          assert.equal(
            jane.title,
            'CTO'
          );
          assert.equal(
            jane.describe(),
            'Person named Jane (CTO)'
          );
          084
    • class Employee extends Person {
        constructor(firstName, title) {
          super(firstName);
          this.title = title; // (C)
        }
        describe() {
          return super.describe() +
            ` (${this.title})`;
        }
      }
      
      const jane = new Employee('Jane', 'CTO');
      assert.equal(
        jane.title,
        'CTO'
      );
      assert.equal(
        jane.describe(),
        'Person named Jane (CTO)'
      );
      085. Người nhận có thuộc tính riêng với khóa đã cho không?
      • Sử dụng phương pháp này không được khuyến khích. Thay thế trong ES2022 trở lên.
        class Employee extends Person {
          constructor(firstName, title) {
            super(firstName);
            this.title = title; // (C)
          }
          describe() {
            return super.describe() +
              ` (${this.title})`;
          }
        }
        
        const jane = new Employee('Jane', 'CTO');
        assert.equal(
          jane.title,
          'CTO'
        );
        assert.equal(
          jane.describe(),
          'Person named Jane (CTO)'
        );
        086

Trước khi xem xét kỹ hơn từng tính năng này, chúng ta sẽ tìm hiểu về một cạm bẫy quan trọng (và cách khắc phục nó). Chúng tôi không thể sử dụng các tính năng của

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
87 với tất cả các đối tượng

29. 8. 1 Sử dụng phương pháp
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
87 một cách an toàn

Gọi một trong các phương thức của

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
87 trên một đối tượng tùy ý không phải lúc nào cũng hoạt động. Để minh họa lý do tại sao, chúng tôi sử dụng phương thức
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
090, trả về
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
091 nếu một đối tượng có thuộc tính riêng với khóa đã cho

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
04

Gọi

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
085 trên một đối tượng tùy ý có thể thất bại theo hai cách. Một mặt, phương thức này không khả dụng nếu một đối tượng không phải là một thể hiện của
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
79 (xem §29. 7. 3 “Không phải tất cả các đối tượng đều là phiên bản của
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
79”)

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
05

Mặt khác, chúng ta không thể sử dụng

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
085 nếu một đối tượng ghi đè lên nó bằng một thuộc tính riêng (dòng A)

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
06

Tuy nhiên, có một cách an toàn để sử dụng

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
085

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
07

Lời gọi phương thức trong dòng A được giải thích trong §29. 3. 5 “Công văn vs. gọi phương thức trực tiếp”

Chúng tôi cũng có thể sử dụng

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
097 để thực hiện
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
098

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
08

Cái này hoạt động ra sao? . Tuy nhiên, nếu chúng ta muốn gọi hàm, chúng ta không thể trích xuất nó một cách đơn giản, chúng ta cũng phải đảm bảo rằng

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
22 của nó luôn có giá trị phù hợp. Đó là những gì
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
097 làm

Có bao giờ sử dụng các phương thức

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
87 thông qua công văn động không?

Trong một số trường hợp, chúng ta có thể lười biếng và gọi các phương thức

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
87 như các phương thức bình thường (không có
class Person {
  #firstName; // (A)
  constructor(firstName) {
    this.#firstName = firstName; // (B)
  }
  describe() {
    return `Person named ${this.#firstName}`;
  }
  static extractNames(persons) {
    return persons.map(person => person.#firstName);
  }
}
04 hoặc
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
097). Nếu chúng ta biết người nhận và chúng là những đối tượng có bố cục cố định

Mặt khác, nếu chúng tôi không biết người nhận của họ và/hoặc họ là đối tượng từ điển, thì chúng tôi cần đề phòng

29. 8. 2 ____1607

Bằng cách ghi đè

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
078 (trong một lớp con hoặc một thể hiện), chúng ta có thể định cấu hình cách các đối tượng được chuyển đổi thành chuỗi

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
09

Để chuyển đổi các đối tượng thành chuỗi, tốt hơn là sử dụng

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
609 vì nó cũng hoạt động với
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
610 và
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
031

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
00

29. 8. 3 ____1612

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
079 là phiên bản của
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
078 có thể được định cấu hình thông qua ngôn ngữ và thường là các tùy chọn bổ sung. Bất kỳ lớp hoặc cá thể nào cũng có thể triển khai phương thức này. Trong thư viện tiêu chuẩn, các lớp sau đây làm

  • class Employee extends Person {
      constructor(firstName, title) {
        super(firstName);
        this.title = title; // (C)
      }
      describe() {
        return super.describe() +
          ` (${this.title})`;
      }
    }
    
    const jane = new Employee('Jane', 'CTO');
    assert.equal(
      jane.title,
      'CTO'
    );
    assert.equal(
      jane.describe(),
      'Person named Jane (CTO)'
    );
    615
  • class Employee extends Person {
      constructor(firstName, title) {
        super(firstName);
        this.title = title; // (C)
      }
      describe() {
        return super.describe() +
          ` (${this.title})`;
      }
    }
    
    const jane = new Employee('Jane', 'CTO');
    assert.equal(
      jane.title,
      'CTO'
    );
    assert.equal(
      jane.describe(),
      'Person named Jane (CTO)'
    );
    616
  • class Employee extends Person {
      constructor(firstName, title) {
        super(firstName);
        this.title = title; // (C)
      }
      describe() {
        return super.describe() +
          ` (${this.title})`;
      }
    }
    
    const jane = new Employee('Jane', 'CTO');
    assert.equal(
      jane.title,
      'CTO'
    );
    assert.equal(
      jane.describe(),
      'Person named Jane (CTO)'
    );
    617
  • class Employee extends Person {
      constructor(firstName, title) {
        super(firstName);
        this.title = title; // (C)
      }
      describe() {
        return super.describe() +
          ` (${this.title})`;
      }
    }
    
    const jane = new Employee('Jane', 'CTO');
    assert.equal(
      jane.title,
      'CTO'
    );
    assert.equal(
      jane.describe(),
      'Person named Jane (CTO)'
    );
    618
  • class Employee extends Person {
      constructor(firstName, title) {
        super(firstName);
        this.title = title; // (C)
      }
      describe() {
        return super.describe() +
          ` (${this.title})`;
      }
    }
    
    const jane = new Employee('Jane', 'CTO');
    assert.equal(
      jane.title,
      'CTO'
    );
    assert.equal(
      jane.describe(),
      'Person named Jane (CTO)'
    );
    619

Ví dụ: đây là cách các số có phân số thập phân được chuyển đổi thành chuỗi khác nhau, tùy thuộc vào ngôn ngữ (

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
620 là tiếng Pháp,
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
621 là tiếng Anh)

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
01

29. 8. 4 ____1622

Bằng cách ghi đè

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
081 (trong một lớp con hoặc một thể hiện), chúng ta có thể định cấu hình cách các đối tượng được chuyển đổi thành giá trị không phải chuỗi (thường là số)

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
02

29. 8. 5 ____1624

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
625 trả về
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
091 nếu
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
627 nằm trong chuỗi nguyên mẫu của
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
29 và ngược lại là
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
021

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
03

Đây là cách sử dụng phương pháp này một cách an toàn (chi tiết xem §29. 8. 1 “Sử dụng phương pháp

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
87 một cách an toàn”)

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
04

29. 8. 6 ____1631

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
632 trả về
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
091 nếu
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
29 có thuộc tính đếm được riêng có khóa là
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
635 và ngược lại là
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
021

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
05

Đây là cách sử dụng phương pháp này một cách an toàn (chi tiết xem §29. 8. 1 “Sử dụng phương pháp

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
87 một cách an toàn”)

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
06

Một giải pháp thay thế an toàn khác là sử dụng các bộ mô tả thuộc tính

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
07

29. 8. 7 
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
638 (người truy cập)

Thuộc tính

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
639 tồn tại trong hai phiên bản

  • Một trình truy cập mà tất cả các phiên bản của
    class Employee extends Person {
      constructor(firstName, title) {
        super(firstName);
        this.title = title; // (C)
      }
      describe() {
        return super.describe() +
          ` (${this.title})`;
      }
    }
    
    const jane = new Employee('Jane', 'CTO');
    assert.equal(
      jane.title,
      'CTO'
    );
    assert.equal(
      jane.describe(),
      'Person named Jane (CTO)'
    );
    79 đều có
  • Một thuộc tính của các đối tượng bằng chữ đặt các nguyên mẫu của các đối tượng do chúng tạo ra

Tôi khuyên bạn nên tránh tính năng cũ

Ngược lại,

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
639 theo nghĩa đen của đối tượng luôn hoạt động và không bị phản đối

Đọc tiếp nếu bạn quan tâm đến cách thức hoạt động của trình truy cập

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
639

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
639 là một bộ truy cập của
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
87 được kế thừa bởi tất cả các phiên bản của
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
79. Thực hiện nó thông qua một lớp sẽ như thế này

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
08

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
639 được kế thừa từ
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
87, chúng tôi có thể loại bỏ tính năng này bằng cách tạo một đối tượng không có
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
87 trong chuỗi nguyên mẫu của nó (xem §29. 7. 3 “Không phải tất cả các đối tượng đều là phiên bản của
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
79”)

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
09

29. 8. 8 ____1650

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
651 trả về
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
091 nếu
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
29 có thuộc tính riêng (không được thừa kế) có khóa là
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
635 và ngược lại là
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
021

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
10

Đây là cách sử dụng phương pháp này một cách an toàn (chi tiết xem §29. 8. 1 “Sử dụng phương pháp

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
87 một cách an toàn”)

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
11

29. 9 Câu hỏi thường gặp. các lớp học

29. 9. 1 Tại sao chúng được gọi là “trường cá thể riêng” trong cuốn sách này mà không phải là “trường cá thể riêng”?

Điều đó được thực hiện để làm nổi bật các thuộc tính khác nhau (các vị trí công khai) và các vị trí riêng tư như thế nào. Bằng cách thay đổi thứ tự của các tính từ, các từ “public” và “field” và các từ “riêng tư” và “field” luôn được đề cập cùng nhau

29. 9. 2 Tại sao lại là tiền tố định danh
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
24?

Các trường riêng tư có thể được khai báo qua

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
658 và sử dụng số nhận dạng thông thường không?

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
12

Bất cứ khi nào một biểu thức chẳng hạn như

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
660 xuất hiện trong phần thân của
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
36, JavaScript phải quyết định

  • class Employee extends Person {
      constructor(firstName, title) {
        super(firstName);
        this.title = title; // (C)
      }
      describe() {
        return super.describe() +
          ` (${this.title})`;
      }
    }
    
    const jane = new Employee('Jane', 'CTO');
    assert.equal(
      jane.title,
      'CTO'
    );
    assert.equal(
      jane.describe(),
      'Person named Jane (CTO)'
    );
    662 có phải là tài sản không?
  • class Employee extends Person {
      constructor(firstName, title) {
        super(firstName);
        this.title = title; // (C)
      }
      describe() {
        return super.describe() +
          ` (${this.title})`;
      }
    }
    
    const jane = new Employee('Jane', 'CTO');
    assert.equal(
      jane.title,
      'CTO'
    );
    assert.equal(
      jane.describe(),
      'Person named Jane (CTO)'
    );
    662 có phải là trường riêng không?

Tại thời điểm biên dịch, JavaScript không biết liệu khai báo trong dòng A có áp dụng cho

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
664 (do nó là phiên bản của
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
36) hay không. Điều đó để lại hai lựa chọn để đưa ra quyết định

  1. class Employee extends Person {
      constructor(firstName, title) {
        super(firstName);
        this.title = title; // (C)
      }
      describe() {
        return super.describe() +
          ` (${this.title})`;
      }
    }
    
    const jane = new Employee('Jane', 'CTO');
    assert.equal(
      jane.title,
      'CTO'
    );
    assert.equal(
      jane.describe(),
      'Person named Jane (CTO)'
    );
    662 luôn được hiểu là trường riêng
  2. JavaScript quyết định trong thời gian chạy
    • Nếu
      class Employee extends Person {
        constructor(firstName, title) {
          super(firstName);
          this.title = title; // (C)
        }
        describe() {
          return super.describe() +
            ` (${this.title})`;
        }
      }
      
      const jane = new Employee('Jane', 'CTO');
      assert.equal(
        jane.title,
        'CTO'
      );
      assert.equal(
        jane.describe(),
        'Person named Jane (CTO)'
      );
      664 là một thể hiện của
      class Employee extends Person {
        constructor(firstName, title) {
          super(firstName);
          this.title = title; // (C)
        }
        describe() {
          return super.describe() +
            ` (${this.title})`;
        }
      }
      
      const jane = new Employee('Jane', 'CTO');
      assert.equal(
        jane.title,
        'CTO'
      );
      assert.equal(
        jane.describe(),
        'Person named Jane (CTO)'
      );
      36, thì
      class Employee extends Person {
        constructor(firstName, title) {
          super(firstName);
          this.title = title; // (C)
        }
        describe() {
          return super.describe() +
            ` (${this.title})`;
        }
      }
      
      const jane = new Employee('Jane', 'CTO');
      assert.equal(
        jane.title,
        'CTO'
      );
      assert.equal(
        jane.describe(),
        'Person named Jane (CTO)'
      );
      662 được hiểu là trường riêng
    • Mặt khác,
      class Employee extends Person {
        constructor(firstName, title) {
          super(firstName);
          this.title = title; // (C)
        }
        describe() {
          return super.describe() +
            ` (${this.title})`;
        }
      }
      
      const jane = new Employee('Jane', 'CTO');
      assert.equal(
        jane.title,
        'CTO'
      );
      assert.equal(
        jane.describe(),
        'Person named Jane (CTO)'
      );
      662 được hiểu là thuộc tính

Cả hai lựa chọn đều có nhược điểm

  • Với tùy chọn (1), chúng tôi không thể sử dụng
    class Employee extends Person {
      constructor(firstName, title) {
        super(firstName);
        this.title = title; // (C)
      }
      describe() {
        return super.describe() +
          ` (${this.title})`;
      }
    }
    
    const jane = new Employee('Jane', 'CTO');
    assert.equal(
      jane.title,
      'CTO'
    );
    assert.equal(
      jane.describe(),
      'Person named Jane (CTO)'
    );
    662 làm thuộc tính nữa – cho bất kỳ đối tượng nào
  • Với tùy chọn (2), hiệu suất bị ảnh hưởng tiêu cực

Đó là lý do tại sao tiền tố tên

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
24 được giới thiệu. Quyết định bây giờ thật dễ dàng. Nếu chúng tôi sử dụng
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
24, chúng tôi muốn truy cập vào một trường riêng tư. Nếu không, chúng tôi muốn truy cập vào một thuộc tính

class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
658 hoạt động cho các ngôn ngữ được nhập tĩnh (chẳng hạn như TypeScript) vì chúng biết tại thời điểm biên dịch nếu
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
664 là một phiên bản của
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
36 và sau đó có thể coi
class Employee extends Person {
  constructor(firstName, title) {
    super(firstName);
    this.title = title; // (C)
  }
  describe() {
    return super.describe() +
      ` (${this.title})`;
  }
}

const jane = new Employee('Jane', 'CTO');
assert.equal(
  jane.title,
  'CTO'
);
assert.equal(
  jane.describe(),
  'Person named Jane (CTO)'
);
662 là riêng tư hoặc công khai

Siêu lớp trong JavaScript là gì?

Định nghĩa và cách sử dụng. Từ khóa super được sử dụng để gọi hàm tạo của lớp cha để truy cập các thuộc tính và phương thức của lớp cha . Mẹo. Để hiểu rõ hơn về khái niệm "kế thừa" (lớp cha và lớp con), hãy đọc Hướng dẫn về lớp JavaScript của chúng tôi.

Sự khác biệt giữa siêu lớp và lớp con là gì?

In Java, it is possible to inherit attributes and methods from one class to another. Chúng tôi nhóm "khái niệm thừa kế" thành hai loại. lớp con (con) - lớp kế thừa từ lớp khác . siêu lớp (cha) - lớp được kế thừa từ .

Phân lớp trong JavaScript là gì?

Phân lớp là một thuật ngữ đề cập đến kế thừa các thuộc tính cho một đối tượng mới từ một đối tượng cơ sở hoặc lớp cha . Trong lập trình hướng đối tượng truyền thống, một lớp B có thể mở rộng một lớp A khác. Ở đây chúng ta coi A là lớp cha và B là lớp con của A. Như vậy, tất cả các phiên bản của B đều kế thừa các phương thức từ A.

Lớp cha và lớp con trong kế thừa là gì?

Sự định nghĩa. Lớp con là lớp kế thừa từ lớp khác. Một lớp con kế thừa trạng thái và hành vi từ tất cả các lớp con của nó. Thuật ngữ siêu lớp đề cập đến tổ tiên trực tiếp của một lớp cũng như tất cả các lớp tăng dần của nó