Bagaimana cara membuat "bidang statis publik" di kelas ES6?

86

Saya membuat kelas Javascript dan saya ingin memiliki bidang statis publik seperti di Java. Ini adalah kode yang relevan:

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

Ini adalah kesalahan yang saya dapatkan:

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

Sepertinya modul ES6 tidak mengizinkan ini. Adakah cara untuk mendapatkan perilaku yang diinginkan atau apakah saya harus menulis yang getter?

aebabis
sumber
Implementasi mesin ECMAScript 6 mana yang Anda gunakan?
Dai

Jawaban:

136

Anda membuat "bidang statis publik" menggunakan pengakses dan kata kunci "statis":

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

Agent.CIRCLE; // 1

Melihat spesifikasi, 14.5 - Definisi Kelas - Anda akan melihat sesuatu yang relevan dan mencurigakan :)

ClassElement [Hasil]:
  MethodDefinition [? Hasil]
  statis MethodDefinition [? Hasil];

Jadi dari sana Anda dapat mengikuti ke 14.5.14 - Runtime Semantics: ClassDefinitionEvaluation - untuk memeriksa ulang apakah benar-benar berfungsi seperti yang diharapkan. Secara khusus, langkah 20:

  1. Untuk setiap ClassElement m diurutkan dari metode
    1. Jika IsStatic dari m salah , maka
      1. Biarkan status menjadi hasil dari melakukan PropertyDefinitionEvaluation untuk m dengan argumen proto dan false.
    2. Lain,
      1. Misalkan status hasil dari melakukan PropertyDefinitionEvaluation untuk m dengan argumen F dan false.
    3. Jika status adalah penyelesaian mendadak, maka
      1. Setel LexicalEnvironment konteks eksekusi yang sedang berjalan ke lex.
      2. Status pengembalian.

IsStatic didefinisikan sebelumnya di 14.5.9

ClassElement: static MethodDefinition
Return benar.

Jadi PropertyMethodDefinitiondisebut dengan "F" (konstruktor, objek fungsi) sebagai argumen, yang pada gilirannya membuat metode pengakses pada objek itu .

Ini sudah berfungsi setidaknya di IETP (pratinjau teknis), serta kompiler 6to5 dan Traceur.

kangax
sumber
Bagi orang lain yang mencari, properti pengakses statis belum didukung di Node. : - / kangax.github.io/compat-table/es6/…
David Hernandez
1
Setidaknya sejak Node.js 6.x +, ini didukung.
NuSkooler
Perhatikan bahwa jika Anda menggunakan flow, Anda harus menambahkan baris unsafe.enable_getters_and_setters=trueke .flowconfig Anda di bawah [options](yang mengganggu).
kristina
Ini tidak akan berhasil untuk saya. Saya mendapatkan pesan `` Unhandled rejection TypeError: Can't set property dataHashKey of class Collections {api_1 | statis dapatkan dataHashKey () {api_1 | mengembalikan 'koleksi'; api_1 | } ``
Pavan
54

Ada proposal ECMAScript Tahap 3 yang disebut "Fitur Kelas Statis" oleh Daniel Ehrenberg dan Jeff Morrison yang bertujuan untuk memecahkan masalah ini. Bersama dengan proposal "Bidang Kelas" Tahap 3 , kode masa depan akan terlihat seperti ini:

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'
    }
}

Di atas sama dengan:

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 mendukung transpiling bidang kelas melalui @ babel / plugin-proposal-class-properties (disertakan dalam preset stage-3 ), sehingga Anda dapat menggunakan fitur ini meskipun runtime JavaScript Anda tidak mendukungnya.


Dibandingkan dengan solusi @ kangax untuk mendeklarasikan getter, solusi ini juga bisa lebih berkinerja, karena di sini properti diakses secara langsung daripada melalui pemanggilan fungsi.

Jika proposal ini diterima, maka dimungkinkan untuk menulis kode JavaScript dengan cara yang lebih mirip dengan bahasa berorientasi objek tradisional seperti Java dan C♯.


Edit : Proposal bidang kelas terpadu sekarang berada di tahap 3; perbarui ke paket Babel v7.x.

Sunting (Feb 2020) : Fitur kelas statis telah dibagi menjadi proposal yang berbeda. Terima kasih @ GOTO0!

Timothy Gu
sumber
Saya pikir proposal yang relevan sebenarnya adalah yang ini ( fitur kelas statis ).
GOTO 0
29

Dalam draf ECMAScript 6 saat ini (per Februari 2015), semua properti kelas harus berupa metode, bukan nilai (perhatikan dalam ECMAScript "properti" memiliki konsep yang mirip dengan bidang OOP, kecuali nilai bidang harus berupa Functionobjek, bukan nilai apa pun nilai lain seperti a Numberatau Object).

Anda masih dapat menentukan ini menggunakan penentu properti konstruktor ECMAScript tradisional:

 class Agent {
 }
 Agent.CIRCLE = 1;
 Agent.SQUARE = 2;
 ...
Dai
sumber
11
Perhatikan bahwa classsintaks ES6 hanyalah gula sintaksis untuk fungsi dan prototipe konstruktor JS tradisional.
Matt Browne
Saya pikir Anda ingin meletakkan properti tersebut pada prototipe dan bukan pada konstruktor agar dapat terlihat melalui referensi properti dari instance.
Pointy
@ Pointy Saya menyimpulkan bahwa OP mencoba menyimpan konstanta untuk referensi (hampir seperti C # /. NET enum).
Dai
2
@MattBrowne Ya, tetapi untuk memperjelas classsintaksis juga memiliki perbedaan nuansa tertentu. Misalnya, metode yang dideklarasikan dengan Class.prototype.method = function () {};dapat dihitung (terlihat dengan loop for-in), sedangkan classmetode tidak dapat dihitung.
Timothy Gu
4

Untuk mendapatkan keuntungan penuh dari variabel statis saya mengikuti pendekatan ini. Untuk lebih spesifik, kita bisa menggunakannya untuk menggunakan variabel privat atau hanya memiliki pengambil publik, atau memiliki keduanya pengambil atau penyetel. Dalam kasus terakhir, ini sama dengan salah satu solusi yang diposting di atas.

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'];

Saya dapat membuat kelas lain yang memperluas Url dan berhasil.

Saya menggunakan babel untuk mengubah kode ES6 saya ke ES5

SM Adnan
sumber
1
Apa yang dimaksud dengan "keuntungan penuh"? Bukankah class Url { static getQueries… }; Url.staticMember = [];jauh lebih sederhana?
Bergi
Mereka ===perbandingan kedua hasil false, btw
Bergi
"Keuntungan Penuh" berarti, dengan cara di atas, Anda dapat membuat _staticMember sebagai pribadi, jika Anda mau.
SM Adnan
-1

Jawaban @kangax tidak meniru seluruh perilaku statis bahasa OOP tradisional, karena Anda tidak dapat mengakses properti statis dengan contoh seperti itu const agent = new Agent; agent.CIRCLE; // Undefined

Jika Anda ingin mengakses properti statis seperti OOP, berikut adalah solusi saya:

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

NewApp.MULTIPLE_VERSIONS_SUPPORTED = true;

Kode tes sebagai berikut.

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

legend80s
sumber
1
Mengakses staticbidang dengan sebuah contoh akan agak tidak biasa, bukan? Dalam beberapa bahasa, seperti Java, IDE sebenarnya mengeluarkan peringatan / petunjuk jika Anda melakukan hal seperti itu.
Isac
@Is Ya, Anda benar. Mengakses dengan contoh tidak disarankan dan begitu juga jawaban saya. Hanya perspektif lain dari solusi tersebut. 😀
legend80s