Làm cách nào để tạo “trường tĩnh công cộng” trong lớp ES6?


86

Tôi đang tạo một lớp Javascript và tôi muốn có một trường tĩnh công khai như trong Java. Đây là mã có liên quan:

export default class Agent {
    CIRCLE: 1,
    SQUARE: 2,
    ...

Đây là lỗi tôi nhận được:

line 2, col 11, Class properties must be methods. Expected '(' but instead saw ':'.

Có vẻ như các mô-đun ES6 không cho phép điều này. Có cách nào để có được hành vi mong muốn hay tôi phải viết một getter?


Bạn đang sử dụng triển khai công cụ ECMAScript 6 nào?
Dai

Câu trả lời:


136

Bạn tạo "trường tĩnh công khai" bằng cách sử dụng công cụ truy cập và từ khóa "tĩnh":

class Agent {
    static get CIRCLE() {
      return 1;
    }
    static get SQUARE() {
      return 2;
    }
}

Agent.CIRCLE; // 1

Nhìn vào thông số kỹ thuật, 14,5 - Định nghĩa lớp - bạn sẽ thấy điều gì đó có liên quan một cách đáng ngờ :)

ClassElement [Yield]:
  MethodDefinition [? Yield]
  static MethodDefinition [? Yield];

Vì vậy, từ đó bạn có thể làm theo 14.5.14 - Ngữ nghĩa thời gian chạy: ClassDefinitionEvaluation - để kiểm tra lại xem nó có thực sự hoạt động như nó không. Cụ thể, bước 20:

  1. Đối với mỗi ClassElement m theo thứ tự từ các phương thức
    1. Nếu IsStatic của m là false , thì
      1. Đặt trạng thái là kết quả của việc thực hiện PropertyDefinitionEvaluation cho m với các đối số là proto và false.
    2. Khác,
      1. Đặt trạng thái là kết quả của việc thực hiện PropertyDefinitionEvaluation cho m với đối số F và false.
    3. Nếu trạng thái hoàn thành đột ngột, thì
      1. Đặt LexicalEn environment của ngữ cảnh thực thi đang chạy thành lex.
      2. Tình trạng trở lại.

IsStatic được định nghĩa trước đó trong 14.5.9

ClassElement: static MethodDefinition
Trả về true.

Vì vậy, PropertyMethodDefinitionđược gọi với "F" (hàm tạo, đối tượng hàm) như một đối số, đến lượt nó sẽ tạo ra một phương thức truy cập trên đối tượng đó .

Điều này đã hoạt động trong ít nhất IETP (xem trước công nghệ), cũng như các trình biên dịch 6to5 và Traceur.


Đối với bất kỳ ai khác đang tìm kiếm, các thuộc tính của trình truy cập tĩnh chưa được hỗ trợ trong Node. : - / kangax.github.io/compat-table/es6/…
David Hernandez

1
Kể từ ít nhất Node.js 6.x +, điều này được hỗ trợ.
NuSkooler

Lưu ý rằng nếu bạn đang sử dụng flow, bạn phải thêm một dòng unsafe.enable_getters_and_setters=truevào .flowconfig của mình bên dưới [options](điều này thật khó chịu).
kristina

Điều này sẽ không hiệu quả đối với tôi, tôi đang nhận được `` Loại từ chối chưa được xử lý Lỗi: Không thể đặt dữ liệu thuộc tínhHashKey của lớp Bộ sưu tập {api_1 | static get dataHashKey () {api_1 | trả về 'bộ sưu tập'; api_1 | } `` '
Pavan

54

Có một đề xuất ECMAScript Giai đoạn 3 được gọi là "Tính năng lớp tĩnh" của Daniel Ehrenberg và Jeff Morrison nhằm giải quyết vấn đề này. Cùng với đề xuất "Trường lớp" Giai đoạn 3 , mã trong tương lai sẽ giống như sau:

class MyClass {
    static myStaticProp = 42;
    myProp = 42;
    myProp2 = this.myProp;
    myBoundFunc = () => { console.log(this.myProp); };

    constructor() {
        console.log(MyClass.myStaticProp); // Prints '42'
        console.log(this.myProp); // Prints '42'
        this.myBoundFunc(); // Prints '42'
    }
}

Ở trên tương đương với:

class MyClass {
    constructor() {
        this.myProp = 42;
        this.myProp2 = this.myProp;
        this.myBoundFunc = () => { console.log(this.myProp); };

        console.log(MyClass.myStaticProp); // Prints '42'
        console.log(this.myProp); // Prints '42'
        this.myBoundFunc(); // Prints '42'
    }
}
MyClass.myStaticProp = 42;

Babel hỗ trợ chuyển đổi các trường lớp thông qua @ babel / plugin- suggest -class-properties (được bao gồm trong cài đặt trước giai đoạn 3 ), do đó bạn có thể sử dụng tính năng này ngay cả khi thời gian chạy JavaScript của bạn không hỗ trợ nó.


So với giải pháp khai báo getter của @ kangax, giải pháp này cũng có thể hoạt động hiệu quả hơn, vì ở đây thuộc tính được truy cập trực tiếp thay vì gọi hàm.

Nếu đề xuất này được chấp nhận, thì có thể viết mã JavaScript theo cách giống với các ngôn ngữ hướng đối tượng truyền thống như Java và C♯.


Chỉnh sửa : Đề xuất các trường lớp thống nhất hiện đang ở giai đoạn 3; cập nhật lên gói Babel v7.x.

Chỉnh sửa (tháng 2 năm 2020) : Các tính năng lớp tĩnh đã được chia thành một đề xuất khác. Cảm ơn @ GOTO0!


Tôi nghĩ rằng đề xuất có liên quan thực sự là đề xuất này ( Các tính năng của lớp tĩnh ).
GOTO 0

29

Trong các bản nháp hiện tại của ECMAScript 6 (kể từ tháng 2 năm 2015), tất cả các thuộc tính của lớp phải là phương thức, không phải giá trị (lưu ý trong ECMAScript, "thuộc tính" có khái niệm tương tự như trường OOP, ngoại trừ giá trị trường phải là một Functionđối tượng, không phải bất kỳ giá trị khác chẳng hạn như a Numberhoặc Object).

Bạn vẫn có thể chỉ định những điều này bằng cách sử dụng các chỉ định thuộc tính phương thức khởi tạo ECMAScript truyền thống:

 class Agent {
 }
 Agent.CIRCLE = 1;
 Agent.SQUARE = 2;
 ...

11
Lưu ý rằng dù sao thì classcú pháp ES6 cũng chỉ là cú pháp cho các hàm tạo JS truyền thống và các nguyên mẫu.
Matt Browne

Tôi nghĩ bạn muốn đặt các thuộc tính đó trên nguyên mẫu chứ không phải trên phương thức khởi tạo để chúng được hiển thị thông qua các tham chiếu thuộc tính từ các phiên bản.
Pointy

@Pointy Tôi suy luận rằng OP đang cố gắng lưu trữ các hằng số để tham chiếu (gần giống như C # /. NET enum).
Đài

2
@MattBrowne Có, nhưng rõ ràng classcú pháp cũng có những khác biệt sắc thái nhất định. Ví dụ, một phương thức được khai báo với Class.prototype.method = function () {};là có thể liệt kê (hiển thị với vòng lặp for-in), trong khi classcác phương thức không thể liệt kê.
Timothy Gu,

4

Để có được đầy đủ lợi thế của biến tĩnh, tôi đã làm theo cách tiếp cận này. Cụ thể hơn, chúng ta có thể sử dụng nó để sử dụng biến private hoặc chỉ có public getter, hoặc có cả getter hoặc setter. Trong trường hợp cuối cùng, nó giống như một trong các giải pháp được đăng ở trên.

var Url = (() => {
    let _staticMember = [];
    return class {
        static getQueries(hash = document.location.hash) {
            return hash;
        }

        static get staticMember(){
            return _staticMember;
        }
    };
})();

Usages:
console.log(Url.staticMember); // [];
Url.staticMember.push('it works');
console.log(Url.staticMember); // ['it works'];

Tôi có thể tạo một lớp mở rộng Url khác và nó đã hoạt động.

Tôi đã sử dụng babel để chuyển đổi mã ES6 của mình thành ES5


1
"Lợi thế đầy đủ" là gì? Sẽ không class Url { static getQueries… }; Url.staticMember = [];đơn giản hơn nhiều phải không?
Bergi

Những ===so sánh đó đều mang lại lợi nhuận false, btw
Bergi

"Full Advantage" có nghĩa là, theo cách trên, bạn có thể giữ _staticMember ở chế độ riêng tư, nếu bạn muốn.
SM Adnan

-1

Câu trả lời của @kangax không bắt chước toàn bộ hành vi tĩnh của ngôn ngữ OOP truyền thống, bởi vì bạn không thể truy cập thuộc tính tĩnh bởi ví dụ của nó như const agent = new Agent; agent.CIRCLE; // Undefined

Nếu bạn muốn truy cập thuộc tính tĩnh giống như của OOP, đây là giải pháp của tôi:

class NewApp {
  get MULTIPLE_VERSIONS_SUPPORTED() {
    return this.constructor.MULTIPLE_VERSIONS_SUPPORTED; // Late binding for inheritance
  }
}

NewApp.MULTIPLE_VERSIONS_SUPPORTED = true;

Mã kiểm tra như sau.

class NewApp {
  get MULTIPLE_VERSIONS_SUPPORTED() {
    console.log('this.constructor.name:', this.constructor.name); // late binding
    return this.constructor.MULTIPLE_VERSIONS_SUPPORTED;
  }
}

// Static property can be accessed by class
NewApp.MULTIPLE_VERSIONS_SUPPORTED = true;

const newApp = new NewApp;

// Static property can be accessed by it's instances
console.log('newApp.MULTIPLE_VERSIONS_SUPPORTED:', newApp.MULTIPLE_VERSIONS_SUPPORTED); // true

// Inheritance
class StandardApp extends NewApp {}

// Static property can be inherited
console.log('StandardApp.MULTIPLE_VERSIONS_SUPPORTED:', StandardApp.MULTIPLE_VERSIONS_SUPPORTED); // true

// Static property can be overwritten
StandardApp.MULTIPLE_VERSIONS_SUPPORTED = false;

const std = new StandardApp;

console.log('std.MULTIPLE_VERSIONS_SUPPORTED:', std.MULTIPLE_VERSIONS_SUPPORTED); // false


1
staticBạn có nói rằng việc truy cập vào một trường theo một trường hợp sẽ không phổ biến không? Trong một số ngôn ngữ, chẳng hạn như Java, IDE thực sự đưa ra cảnh báo / gợi ý nếu bạn làm điều gì đó như vậy.
Isac

@Isac Có bạn nói đúng. Truy cập theo trường hợp không được khuyến khích và câu trả lời của tôi cũng vậy. Chỉ là một góc nhìn khác của giải pháp. 😀
huyền thoại 80s
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.