Mendeklarasikan konstanta statis di kelas ES6?

312

Saya ingin menerapkan konstanta dalam class, karena di situlah masuk akal untuk menempatkan mereka dalam kode.

Sejauh ini, saya telah mengimplementasikan solusi berikut dengan metode statis:

class MyClass {
    static constant1() { return 33; }
    static constant2() { return 2; }
    // ...
}

Saya tahu ada kemungkinan untuk mengutak-atik prototipe, tetapi banyak yang merekomendasikan ini.

Apakah ada cara yang lebih baik untuk mengimplementasikan konstanta di kelas ES6?

Jérôme Verstrynge
sumber
7
Secara pribadi saya hanya menggunakan VARNAM huruf besar, dan mengatakan pada diri saya untuk tidak menyentuh mereka;)
twicejr
3
@twicejr Saya pikir ini tidak sama, untuk variabel statis dapat diakses tanpa terlebih dahulu instantiate objek dari kelas itu?
Lucas Morgan

Jawaban:

386

Berikut beberapa hal yang dapat Anda lakukan:

Ekspor a constdari modul . Tergantung pada kasus penggunaan Anda, Anda bisa saja:

export const constant1 = 33;

Dan impor itu dari modul jika perlu. Atau, membangun ide metode statis Anda, Anda dapat mendeklarasikan static aksesor get :

const constant1 = 33,
      constant2 = 2;
class Example {

  static get constant1() {
    return constant1;
  }

  static get constant2() {
    return constant2;
  }
}

Dengan begitu, Anda tidak perlu tanda kurung:

const one = Example.constant1;

Contoh Babel REPL

Kemudian, seperti yang Anda katakan, karena a classhanya gula sintaksis untuk suatu fungsi, Anda bisa menambahkan properti yang tidak bisa ditulisi seperti:

class Example {
}
Object.defineProperty(Example, 'constant1', {
    value: 33,
    writable : false,
    enumerable : true,
    configurable : false
});
Example.constant1; // 33
Example.constant1 = 15; // TypeError

Mungkin menyenangkan jika kita bisa melakukan sesuatu seperti:

class Example {
    static const constant1 = 33;
}

Tetapi sayangnya sintaksis properti kelas ini hanya ada dalam proposal ES7, dan meskipun demikian itu tidak akan memperbolehkan untuk menambahkan constke properti.

CodingIntrigue
sumber
apakah ada konfirmasi bahwa properti statis dapat dikomputasi sekali untuk hal-hal seperti ini, atau apakah lebih aman untuk menggunakan IIFE dan menambahkan properti secara manual di IIFE untuk menghindari pembangunan berulang nilai pengembalian. Saya khawatir jika hasil dari pengambil benar-benar berat, seperti entri JSObject 100000, maka pengambil miskin harus membangunnya setiap kali pengambil dipanggil. Mudah untuk diuji oleh performance.now/date diff, tetapi mungkin diimplementasikan secara berbeda, yang pasti lebih mudah untuk menerapkan getter sebagai evaluasi literal daripada keputusan lanjutan apakah konstan atau tidak.
Dmitry
3
sementara di atas secara cerdik menambahkan properti konstan ke kelas, nilai aktual untuk konstanta adalah "di luar" definisi kelas "{}", yang benar-benar melanggar salah satu definisi enkapsulasi. Saya kira itu cukup untuk hanya mendefinisikan properti konstan "di dalam" kelas dan tidak perlu untuk mendapatkan dalam hal ini.
NoChance
1
@NoChance Poin bagus. Itu hanya ilustrasi. Tidak ada alasan metode pengambil tidak bisa sepenuhnya merangkum nilai jika diperlukan.
CodingIntrigue
Menantikan untuk menggunakan proposal ES7 karena menurut saya lebih alami dan setara dengan sebagian besar bahasa OO.
Sangimed
Apa yang ingin saya nyatakan sebagai variabel instan? Dapatkah saya melakukan sesuatu sepertithis.defineProperty(this, 'constant1', {...})
Francesco Boi
33
class Whatever {
    static get MyConst() { return 10; }
}

let a = Whatever.MyConst;

Tampaknya bekerja untuk saya.

Benny Jobigan
sumber
Apakah ini dapat diakses di dalam kelas dengan metode normal?
PirateApp
3
@PirateApp Anda dapat mengaksesnya di mana saja sebagai metode statis, bahkan dari dalam instance kelas. Namun, karena ini statis yang tidak dapat Anda gunakan this.MyConstdari dalam Whateverinstance, Anda harus selalu menulisnya seperti ini: Whatever.MyConst
TheDarkIn1978
23

Saya menggunakan babeldan sintaks berikut bekerja untuk saya:

class MyClass {
    static constant1 = 33;
    static constant2 = {
       case1: 1,
       case2: 2,
    };
    // ...
}

MyClass.constant1 === 33
MyClass.constant2.case1 === 1

Harap pertimbangkan bahwa Anda memerlukan preset "stage-0".
Untuk menginstalnya:

npm install --save-dev babel-preset-stage-0

// in .babelrc
{
    "presets": ["stage-0"]
}

Memperbarui:

sedang digunakan stage-3

borracciaBlu
sumber
21
Masalahnya adalah bahwa konstanta dapat dipindahkan. Op tidak menginginkan itu
CodingIntrigue
3
FYI, ini sekarang di babelstage-2
bmaupin
3
itu bukan konstanta
Dave L.
1
@CodingIntrigue Apakah memanggil Object.freeze()kelas memperbaikinya?
Antimony
1
@Antimony Saya belum mengujinya tetapi saya akan berpikir begitu. Masalahnya adalah itu akan berlaku untuk semua properti kelas. Non-statis juga.
CodingIntrigue
14

Dalam dokumen ini disebutkan:

Tidak ada (sengaja) tidak ada cara deklaratif langsung untuk mendefinisikan properti data prototipe (selain metode) kelas properti, atau properti instance

Ini berarti memang sengaja seperti ini.

Mungkin Anda bisa mendefinisikan variabel dalam konstruktor?

constructor(){
    this.key = value
}
DevAlien
sumber
2
Ya, ini bisa berhasil. Juga, saya ingin menyebutkan, bahwa constructor memanggil ketika instance dibuat dan untuk setiap instance this.key tidak akan sama. Metode dan properti statis memungkinkan kita untuk menggunakannya secara langsung dari kelas, tanpa membuat instance. Ada poin bagus dan lemah dari metode / properti statis.
Kirill Gusyatin
1
Konstanta harus tidak berubah. Menetapkan properti pada objek selama konstruksi akan menghasilkan properti yang dapat dimodifikasi.
philraj
11

Hal ini juga memungkinkan untuk menggunakan Object.freezepada Anda kelas (ES6) / konstruktor fungsi (ES5) objek untuk membuatnya berubah:

class MyConstants {}
MyConstants.staticValue = 3;
MyConstants.staticMethod = function() {
  return 4;
}
Object.freeze(MyConstants);
// after the freeze, any attempts of altering the MyConstants class will have no result
// (either trying to alter, add or delete a property)
MyConstants.staticValue === 3; // true
MyConstants.staticValue = 55; // will have no effect
MyConstants.staticValue === 3; // true

MyConstants.otherStaticValue = "other" // will have no effect
MyConstants.otherStaticValue === undefined // true

delete MyConstants.staticMethod // false
typeof(MyConstants.staticMethod) === "function" // true

Mencoba mengubah kelas akan memberi Anda soft-fail (tidak akan membuang kesalahan, itu hanya tidak akan berpengaruh).

rodrigo.botti
sumber
3
Soft-fail itu cukup menakutkan bagi kita yang berasal dari bahasa lain - hanya beradaptasi dengan gagasan bahwa alat tidak banyak membantu kita dalam menemukan kesalahan, sekarang bahkan runtime tidak akan membantu. (Kalau tidak, saya suka solusi Anda.)
Tom
Saya suka Object.freeze()menerapkan imutabilitas, dan telah sering menggunakannya akhir-akhir ini. Hanya saja, jangan lupa untuk menerapkannya secara rekursif!
jeffwtribble
6

Mungkin hanya meletakkan semua konstanta Anda di objek beku?

class MyClass {

    constructor() {
        this.constants = Object.freeze({
            constant1: 33,
            constant2: 2,
        });
    }

    static get constant1() {
        return this.constants.constant1;
    }

    doThisAndThat() {
        //...
        let value = this.constants.constant2;
        //...
    }
}
ARIEL
sumber
Fungsi statis tidak dapat menggunakan variabel 'ini'.
PokerFace
4

Seperti https://stackoverflow.com/users/2784136/rodrigo-botti berkata, saya pikir Anda sedang mencari Object.freeze(). Berikut adalah contoh kelas dengan statika yang tidak dapat diubah:

class User {
  constructor(username, age) {
    if (age < User.minimumAge) {
      throw new Error('You are too young to be here!');
    }
    this.username = username;
    this.age = age;
    this.state = 'active';
  }
}

User.minimumAge = 16;
User.validStates = ['active', 'inactive', 'archived'];

deepFreeze(User);

function deepFreeze(value) {
  if (typeof value === 'object' && value !== null) {
    Object.freeze(value);
    Object.getOwnPropertyNames(value).forEach(property => {
      deepFreeze(value[property]);
    });
  }
  return value;
}
jeffwtribble
sumber
1

Inilah satu lagi cara yang bisa Anda lakukan

/*
one more way of declaring constants in a class,
Note - the constants have to be declared after the class is defined
*/
class Auto{
   //other methods
}
Auto.CONSTANT1 = "const1";
Auto.CONSTANT2 = "const2";

console.log(Auto.CONSTANT1)
console.log(Auto.CONSTANT2);

Catatan - Perintah itu penting, Anda tidak dapat memiliki konstanta di atas

Penggunaan console.log (Auto.CONSTANT1);

pengguna3871424
sumber
5
Mereka tidak berubah
John Harding
1

Anda dapat membuat cara untuk mendefinisikan konstanta statis pada kelas menggunakan fitur aneh kelas ES6. Karena statika diwarisi oleh subkelasnya, Anda dapat melakukan hal berikut:

const withConsts = (map, BaseClass = Object) => {
  class ConstClass extends BaseClass { }
  Object.keys(map).forEach(key => {
    Object.defineProperty(ConstClass, key, {
      value: map[key],
      writable : false,
      enumerable : true,
      configurable : false
    });
  });
  return ConstClass;
};

class MyClass extends withConsts({ MY_CONST: 'this is defined' }) {
  foo() {
    console.log(MyClass.MY_CONST);
  }
}
TbWill4321
sumber
1

Anda dapat membuat "konstanta" read-only (tidak berubah) dengan membekukan kelas. misalnya

class Foo {
    static BAR = "bat"; //public static read-only
}

Object.freeze(Foo); 

/*
Uncaught TypeError: Cannot assign to read only property 'BAR' of function 'class Foo {
    static BAR = "bat"; //public static read-only
}'
*/
Foo.BAR = "wut";
Fraser
sumber
0

Jika Anda merasa nyaman mencampur dan mencocokkan antara sintaks fungsi dan kelas, Anda dapat mendeklarasikan konstanta setelah kelas (konstanta 'diangkat'). Perhatikan bahwa Visual Studio Code akan berjuang untuk memformat sintaksis campuran secara otomatis, (meskipun berfungsi).

class MyClass {
    // ...

}
MyClass.prototype.consts = { 
    constant1:  33,
    constant2: 32
};
mc = new MyClass();
console.log(mc.consts.constant2);    

Cam Cairns
sumber
0

Saya melakukan ini.

class Circle
{
    constuctor(radius)
    {
        this.radius = radius;
    }
    static get PI()
    {
        return 3.14159;
    }
}

Nilai PI dilindungi dari perubahan karena itu adalah nilai yang dikembalikan dari suatu fungsi. Anda dapat mengaksesnya melalui Circle.PI. Setiap upaya untuk menetapkannya hanya dijatuhkan di lantai dengan cara yang mirip dengan upaya untuk menetapkan ke karakter string melalui [].

ncmathsadist
sumber
0

Anda dapat mendefinisikannya seperti ini:

class Foo {
  static MyConst = 200;

  myFunc() {
    const doubleConst = Foo.MyConst * 2;
  }
}
zmekanik
sumber
0

Anda bisa menggunakan import * assintaks. Meskipun bukan kelas, mereka adalah constvariabel nyata .

Constants.js

export const factor = 3;
export const pi = 3.141592;

index.js

import * as Constants from 'Constants.js'
console.log( Constants.factor );
Vincent
sumber