Khai báo hằng số tĩnh trong các lớp ES6?


311

Tôi muốn triển khai các hằng số trong một class, bởi vì đó là nơi hợp lý để xác định vị trí của chúng trong mã.

Cho đến nay, tôi đã thực hiện cách giải quyết sau với các phương thức tĩnh:

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

Tôi biết có khả năng nghịch ngợm với các nguyên mẫu, nhưng nhiều người khuyên bạn nên chống lại điều này.

Có cách nào tốt hơn để thực hiện các hằng số trong các lớp ES6 không?


7
Cá nhân tôi chỉ sử dụng chữ hoa chữ thường và tự nhủ mình không chạm vào chúng;)
hai lần vào

3
@twicejr Tôi nghĩ rằng điều này không giống nhau, vì các biến tĩnh có thể được truy cập mà không cần khởi tạo một đối tượng của lớp đó trước?
Lucas Morgan

Câu trả lời:


385

Dưới đây là một vài điều bạn có thể làm:

Xuất a consttừ mô-đun . Tùy thuộc vào trường hợp sử dụng của bạn, bạn có thể chỉ cần:

export const constant1 = 33;

Và nhập nó từ các mô-đun khi cần thiết. Hoặc, xây dựng trên phương pháp ý tưởng tĩnh của bạn, bạn có thể khai báo một static get accessor :

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

  static get constant1() {
    return constant1;
  }

  static get constant2() {
    return constant2;
  }
}

Theo cách đó, bạn sẽ không cần dấu ngoặc đơn:

const one = Example.constant1;

Ví dụ REPL Babel

Sau đó, như bạn nói, vì a classchỉ là đường cú pháp cho một hàm, bạn có thể chỉ cần thêm một thuộc tính không thể ghi như vậy:

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

Nó có thể tốt nếu chúng ta có thể làm một cái gì đó như:

class Example {
    static const constant1 = 33;
}

Nhưng thật không may, cú pháp thuộc tính lớp này chỉ có trong một đề xuất ES7 và thậm chí sau đó nó sẽ không cho phép thêm constvào thuộc tính.


Có bất kỳ xác nhận nào rằng các thuộc tính tĩnh được tính toán một lần cho những thứ như thế này hay an toàn hơn khi sử dụng IIFE và thêm thuộc tính thủ công trong IIFE để tránh xây dựng lại các giá trị trả về. Tôi lo lắng rằng nếu kết quả của getter thực sự nặng nề, giống như một JSObject 100000 mục nhập, thì getter nghèo sẽ phải xây dựng nó mỗi khi getter được gọi. Thật dễ dàng để kiểm tra bằng Performance.now/date diff, nhưng nó có thể được triển khai theo cách khác, dễ dàng hơn để thực hiện getters như đánh giá theo nghĩa đen thay vì các quyết định nâng cao cho dù nó có liên tục hay không.
Dmitry

3
trong khi ở trên khéo léo thêm một thuộc tính hằng vào một lớp, giá trị thực của hằng là "bên ngoài" định nghĩa lớp "{}", thực sự vi phạm một trong các định nghĩa về đóng gói. Tôi đoán nó là đủ để chỉ định nghĩa một thuộc tính không đổi "bên trong" lớp và không cần lấy trong trường hợp này.
NoChance

1
@Không Điểm tốt. Đó chỉ là minh họa. Không có lý do gì phương thức getter không thể đóng gói đầy đủ giá trị nếu được yêu cầu.
Mã hóa

Nhìn về phía trước để sử dụng đề xuất ES7 vì nó trông tự nhiên hơn và tương đương với phần lớn các ngôn ngữ OO.
Tăng

Những gì tôi muốn khai báo hằng số một biến? Tôi có thể làm điều gì đó nhưthis.defineProperty(this, 'constant1', {...})
Francesco Boi

33
class Whatever {
    static get MyConst() { return 10; }
}

let a = Whatever.MyConst;

Dường như làm việc cho tôi.


điều này có thể truy cập bên trong lớp trong một phương thức bình thường không?
PirateApp

3
@PirateApp bạn có thể truy cập nó ở bất cứ đâu như một phương thức tĩnh, thậm chí từ bên trong một thể hiện của lớp. Tuy nhiên, vì nó tĩnh mà bạn không thể sử dụng this.MyConsttừ bên trong một Whateverví dụ, bạn luôn phải viết nó như thế này: Whatever.MyConst
TheDarkIn1978

23

Tôi đang sử dụng babelvà cú pháp sau đây đang làm việc cho tôi:

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

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

Hãy xem xét rằng bạn cần cài đặt trước "stage-0".
Để cài đặt nó:

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

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

Cập nhật:

hiện đang sử dụng stage-3


21
Vấn đề là hằng số được gán lại. Op không muốn điều đó
CodingIntrigue 27/8/2016

3
FYI, đây là lúc babelstage-2
bmaupin

3
những hằng số đó
Dave L.

1
@CodingIntrigue Sẽ kêu gọi Object.freeze()lớp sửa lỗi đó?
Antimon

1
@Antimony Tôi chưa thử nghiệm điều đó nhưng tôi sẽ nghĩ vậy. Vấn đề là nó sẽ áp dụng cho tất cả các thuộc tính của lớp. Không tĩnh quá.
Mã hóa

14

Trong tài liệu này có ghi:

(Cố ý) không có cách khai báo trực tiếp để định nghĩa các thuộc tính dữ liệu nguyên mẫu (trừ các phương thức) hoặc các thuộc tính thể hiện

Điều này có nghĩa là nó cố ý như thế này.

Có lẽ bạn có thể định nghĩa một biến trong hàm tạo?

constructor(){
    this.key = value
}

2
Vâng, điều này có thể làm việc. Ngoài ra, tôi muốn đề cập, hàm tạo đó gọi khi cá thể được tạo và đối với mỗi phiên bản this.key sẽ không giống nhau. Phương thức và thuộc tính tĩnh cho phép chúng ta sử dụng chúng trực tiếp từ lớp mà không cần tạo cá thể. Có những điểm tốt và yếu của các phương thức / thuộc tính tĩnh.
Kirill Gusyatin

1
Hằng số nên bất biến. Việc gán cho các thuộc tính trên đối tượng trong quá trình xây dựng sẽ mang lại các thuộc tính có thể được sửa đổi.
philraj

11

Cũng có thể sử dụng Object.freezetrên đối tượng lớp (es6) / hàm tạo (es5) của bạn để làm cho nó bất biến:

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

Cố gắng thay đổi lớp sẽ mang lại cho bạn một lỗi mềm (sẽ không gây ra bất kỳ lỗi nào, đơn giản là nó sẽ không có hiệu lực).


3
Sự thất bại mềm mại đó khá đáng sợ đối với những người trong chúng ta đến từ các ngôn ngữ khác - chỉ thích nghi với ý tưởng rằng các công cụ không giúp chúng ta nhiều trong việc tìm kiếm lỗi, ngay cả thời gian chạy cũng không giúp được gì. (Nếu không, tôi thích giải pháp của bạn.)
Tom

Tôi thích Object.freeze()thực thi sự bất biến, và đã sử dụng nó rất nhiều gần đây. Chỉ cần đừng quên áp dụng đệ quy!
jeffwtribble

6

Có lẽ chỉ cần đặt tất cả các hằng số của bạn trong một đối tượng đông lạnh?

class MyClass {

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

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

    doThisAndThat() {
        //...
        let value = this.constants.constant2;
        //...
    }
}

Hàm tĩnh không thể sử dụng biến 'this'.
PokerFace

4

Giống như https://stackoverflow.com/users/2784136/rodrigo-botti đã nói, tôi nghĩ bạn đang tìm kiếm Object.freeze(). Đây là một ví dụ về một lớp học với số liệu thống kê bất biến:

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

1

Đây là một cách nữa bạn có thể làm

/*
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);

Lưu ý - Đơn hàng rất quan trọng, bạn không thể có các hằng số ở trên

Bảng điều khiển sử dụng.log (Auto.CONSTANT1);


5
Mặc dù vậy, chúng không phải là bất biến
John Harding

1

Bạn có thể tạo một cách để xác định các hằng số tĩnh trên một lớp bằng cách sử dụng một tính năng kỳ lạ của các lớp ES6. Vì statics được kế thừa bởi các lớp con của chúng, bạn có thể làm như sau:

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);
  }
}

1

Bạn có thể làm cho "hằng số" chỉ đọc (không thay đổi) bằng cách đóng băng lớp. ví dụ

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";

0

Nếu bạn cảm thấy thoải mái khi trộn và khớp giữa hàm và cú pháp lớp, bạn có thể khai báo các hằng số sau lớp (các hằng số được 'nâng lên'). Lưu ý rằng Visual Studio Code sẽ đấu tranh để tự động định dạng cú pháp hỗn hợp, (mặc dù nó hoạt động).

class MyClass {
    // ...

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


0

Tôi đã làm điều này.

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

Giá trị của PI được bảo vệ khỏi bị thay đổi vì đó là giá trị được trả về từ hàm. Bạn có thể truy cập nó qua Circle.PI. Bất kỳ nỗ lực nào để gán cho nó chỉ đơn giản là được thả trên sàn theo cách tương tự như một nỗ lực để gán cho một ký tự chuỗi thông qua [].


0

Bạn có thể định nghĩa nó như thế này:

class Foo {
  static MyConst = 200;

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

0

Bạn có thể sử dụng import * ascú pháp. Mặc dù không phải là một lớp, chúng là các constbiến thực .

Constants.js

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

index.js

import * as Constants from 'Constants.js'
console.log( Constants.factor );
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.