Hướng dẫn javascript static property inheritance - kế thừa thuộc tính tĩnh javascript

Kể từ ES6, JavaScript thích hỗ trợ cho các lớp và chức năng tĩnh gần giống với các hàm tĩnh trong các ngôn ngữ hướng đối tượng khác. Thật không may, JavaScript thiếu hỗ trợ cho các thuộc tính tĩnh và các giải pháp được đề xuất trên Google không tính đến kế thừa. Tôi gặp phải vấn đề này khi thực hiện một tính năng Mongoose mới đòi hỏi một khái niệm mạnh mẽ hơn về các thuộc tính tĩnh. Cụ thể, tôi cần các thuộc tính tĩnh hỗ trợ kế thừa thông qua việc cài đặt prototype hoặc thông qua extends. Trong bài viết này, tôi sẽ mô tả một mẫu để thực hiện các thuộc tính tĩnh trong ES6.

Phương pháp tĩnh và kế thừa

Giả sử bạn có một lớp ES6 đơn giản với phương pháp tĩnh.

class Base {
  static foo() {
    return 42;
  }
}

Bạn có thể sử dụng extends để tạo một lớp con và vẫn có quyền truy cập vào chức năng

class Sub extends Base {}

Sub.foo(); // 42
0.

class Sub extends Base {}

Sub.foo(); // 42

Bạn cũng có thể sử dụng các getters và setter tĩnh để đặt thuộc tính tĩnh trên lớp

class Sub extends Base {}

Sub.foo(); // 42
1.

let foo = 42;

class Base {
  static get foo() { return foo; }
  static set foo(v) { foo = v; }
}

Thật không may, mô hình này có hành vi không mong muốn khi bạn phân lớp

class Sub extends Base {}

Sub.foo(); // 42
1. Nếu bạn đặt
class Sub extends Base {}

Sub.foo(); // 42
3 trên một lớp con, nó sẽ đặt
class Sub extends Base {}

Sub.foo(); // 42
3 cho lớp
class Sub extends Base {}

Sub.foo(); // 42
1 và tất cả các lớp con khác.

class Sub extends Base {}

console.log(Base.foo, Sub.foo);

Sub.foo = 43;

// Prints "43, 43". The above set `Base.foo` as well as `Sub.foo`
console.log(Base.foo, Sub.foo);

Vấn đề trở nên tồi tệ hơn nếu tài sản của bạn là một mảng hoặc một đối tượng. Do sự kế thừa nguyên mẫu, nếu

class Sub extends Base {}

Sub.foo(); // 42
3 là một mảng, mọi lớp con sẽ có một tham chiếu đến cùng một bản sao của mảng như hình dưới đây.

class Base {
  static get foo() { return this._foo; }
  static set foo(v) { this._foo = v; }
}

Base.foo = [];

class Sub extends Base {}

console.log(Base.foo, Sub.foo);

Sub.foo.push('foo');

// Both arrays now contain 'foo' because they are the same array!
console.log(Base.foo, Sub.foo);
console.log(Base.foo === Sub.foo); // true

Vì vậy, JavaScript hỗ trợ các getters và setters tĩnh, nhưng sử dụng chúng với các đối tượng hoặc mảng là một bước chân. Hóa ra bạn có thể làm điều đó với một chút trợ giúp từ chức năng

class Sub extends Base {}

Sub.foo(); // 42
7 tích hợp của JavaScript.

Tính chất tĩnh với kế thừa

Ý tưởng chính là một lớp JavaScript chỉ là một đối tượng khác, vì vậy bạn có thể phân biệt giữa các thuộc tính riêng và các thuộc tính được kế thừa.

class Base {
  static get foo() {
    // If `_foo` is inherited or doesn't exist yet, treat it as `undefined`
    return this.hasOwnProperty('_foo') ? this._foo : void 0;
  }
  static set foo(v) { this._foo = v; }
}

Base.foo = [];

class Sub extends Base {}

// Prints "[] undefined"
console.log(Base.foo, Sub.foo);
console.log(Base.foo === Sub.foo); // false

Base.foo.push('foo');

// Prints "['foo'] undefined"
console.log(Base.foo, Sub.foo);
console.log(Base.foo === Sub.foo); // false

Mẫu này gọn gàng với các lớp, nhưng nó cũng hoạt động với kế thừa JavaScript trước ES6. Điều này rất quan trọng vì Mongoose vẫn sử dụng kế thừa kiểu Pre-ES6. Nhìn nhận lại, chúng ta nên chuyển đổi sớm hơn, nhưng tính năng này là lần đầu tiên chúng ta thấy một lợi thế rõ ràng khi sử dụng các lớp ES6 và kế thừa chỉ khi đặt chức năng ____77.

function Base() {}

Object.defineProperty(Base, 'foo', {
  get: function() { return this.hasOwnProperty('_foo') ? this._foo : void 0; },
  set: function(v) { this._foo = v; }
});

Base.foo = [];

// Pre-ES6 inheritance
function Sub1() {}
Sub1.prototype = Object.create(Base.prototype);
// Static properties were annoying pre-ES6
Object.defineProperty(Sub1, 'foo', Object.getOwnPropertyDescriptor(Base, 'foo'));

// ES6 inheritance
class Sub2 extends Base {}

// Prints "[] undefined"
console.log(Base.foo, Sub1.foo);
// Prints "[] undefined"
console.log(Base.foo, Sub2.foo);

Base.foo.push('foo');

// Prints "['foo'] undefined"
console.log(Base.foo, Sub1.foo);
// Prints "['foo'] undefined"
console.log(Base.foo, Sub2.foo);

Tiến lên

Các lớp ES6 có lợi thế lớn so với trường cũ

class Sub extends Base {}

Sub.foo(); // 42
9 vì extends bản sao trên các thuộc tính và chức năng tĩnh. Với một chút công việc sử dụng
let foo = 42;

class Base {
  static get foo() { return foo; }
  static set foo(v) { foo = v; }
}
1, bạn có thể tạo các getters và setter tĩnh xử lý chính xác kế thừa. Hãy rất cẩn thận với các thuộc tính tĩnh trong JavaScript: extends vẫn sử dụng kế thừa nguyên mẫu dưới mui xe. Điều đó có nghĩa là các đối tượng và mảng tĩnh được chia sẻ giữa tất cả các lớp con trừ khi bạn sử dụng mẫu
class Sub extends Base {}

Sub.foo(); // 42
7 từ bài viết này.

Tìm thấy một lỗi đánh máy hoặc lỗi? Mở ra một yêu cầu kéo! Bài đăng này có sẵn dưới dạng Markdown trên & nbsp; github