การประกาศค่าคงที่ในคลาส ES6?


324

ฉันต้องการใช้ค่าคงที่ใน a classเพราะนั่นเป็นสิ่งที่สมเหตุสมผลที่จะหาค่าเหล่านี้ในโค้ด

จนถึงตอนนี้ฉันได้ใช้วิธีแก้ปัญหาต่อไปนี้ด้วยวิธีการคงที่:

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

ฉันรู้ว่ามีความเป็นไปได้ที่จะเล่นซอกับต้นแบบ แต่หลายคนแนะนำให้ต่อต้านสิ่งนี้

มีวิธีที่ดีกว่าในการใช้ค่าคงที่ในคลาส ES6 หรือไม่?


7
โดยส่วนตัวฉันใช้ VARNAMES ตัวพิมพ์ใหญ่และบอกตัวเองว่าอย่าแตะต้องมัน;)
twicejr

3
@twicejr ฉันคิดว่ามันไม่เหมือนกันสำหรับตัวแปรคงที่สามารถเข้าถึงได้โดยไม่ต้องสร้างอินสแตนซ์วัตถุของคลาสนั้นก่อน?
Lucas Morgan

คำตอบ:


397

สิ่งที่คุณทำได้มีดังนี้

การส่งออกconstจากโมดูล ขึ้นอยู่กับกรณีการใช้งานของคุณคุณสามารถ:

export const constant1 = 33;

และนำเข้าจากโมดูลหากจำเป็น หรือสร้างจากแนวคิดวิธีการแบบคงที่ของคุณคุณสามารถประกาศstatic get accessor :

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

  static get constant1() {
    return constant1;
  }

  static get constant2() {
    return constant2;
  }
}

ด้วยวิธีนี้คุณไม่จำเป็นต้องมีวงเล็บ:

const one = Example.constant1;

ตัวอย่าง Babel REPL

จากนั้นอย่างที่คุณพูดเนื่องจาก a classเป็นเพียงแค่น้ำตาลในการสังเคราะห์สำหรับฟังก์ชันคุณสามารถเพิ่มคุณสมบัติที่ไม่สามารถเขียนได้ดังนี้:

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

อาจเป็นการดีถ้าเราสามารถทำสิ่งต่างๆเช่น:

class Example {
    static const constant1 = 33;
}

แต่น่าเสียดายที่ไวยากรณ์คุณสมบัติของคลาสนี้อยู่ในข้อเสนอ ES7 เท่านั้นและถึงแม้ว่ามันจะไม่อนุญาตให้เพิ่มconstคุณสมบัติ


มีการยืนยันหรือไม่ว่าคุณสมบัติคงที่ได้รับการคำนวณเพียงครั้งเดียวสำหรับสิ่งต่างๆเช่นนี้หรือปลอดภัยกว่าที่จะใช้ IIFE และเพิ่มคุณสมบัติด้วยตนเองใน IIFE เพื่อหลีกเลี่ยงการสร้างค่าส่งคืนซ้ำ ฉันกังวลว่าถ้าผลลัพธ์ของ getter นั้นหนักมากเช่น JSObject รายการ 100000 ดังนั้นผู้ที่ไม่ดีจะต้องสร้างมันทุกครั้งที่เรียก getter ง่ายต่อการทดสอบตามประสิทธิภาพตอนนี้ / วันที่แตกต่างกัน แต่อาจมีการใช้งานที่แตกต่างกันการใช้ getters เป็นการประเมินตามตัวอักษรง่ายกว่าการตัดสินใจขั้นสูงว่าคงที่หรือไม่
Dmitry

4
ในขณะที่ข้างต้นเพิ่มคุณสมบัติคงที่ให้กับคลาสอย่างชาญฉลาดค่าที่แท้จริงของค่าคงที่คือ "นอก" นิยามคลาส "{}" ซึ่งละเมิดคำจำกัดความของการห่อหุ้มข้อใดข้อหนึ่ง ฉันเดาว่ามันเพียงพอที่จะกำหนดคุณสมบัติคงที่ "ภายใน" คลาสและไม่จำเป็นต้องได้รับในกรณีนี้
NoChance

1
@NoChance จุดดี. นั่นเป็นเพียงแค่ตัวอย่างเท่านั้น ไม่มีเหตุผลที่เมธอด getter ไม่สามารถสรุปค่าได้อย่างสมบูรณ์หากจำเป็น
CodingIntrigue

รอคอยที่จะใช้ข้อเสนอ ES7 เพราะดูเหมือนว่าฉันจะเป็นธรรมชาติมากกว่าและเทียบเท่ากับภาษา OO ส่วนใหญ่
Sangimed

superคำหลักเป็นบิตน้อยกว่าน้ำตาลประโยค
Rivenfall

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

let a = Whatever.MyConst;

ดูเหมือนว่าจะทำงานสำหรับฉัน


สิ่งนี้สามารถเข้าถึงได้ภายในชั้นเรียนด้วยวิธีการปกติหรือไม่
PirateApp

3
@PirateApp คุณสามารถเข้าถึงได้ทุกที่เป็นวิธีการแบบคงที่แม้จากภายในอินสแตนซ์ของคลาส อย่างไรก็ตามเนื่องจากเป็นแบบคงที่คุณไม่สามารถใช้this.MyConstจากภายในWhateverอินสแตนซ์ได้คุณจึงต้องเขียนแบบนี้เสมอ: Whatever.MyConst
Chunky Chunk

หรือ this.constructor.MyConst
Martijn Scheffer

21

ฉันกำลังใช้babelและไวยากรณ์ต่อไปนี้ใช้ได้ผลสำหรับฉัน:

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

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

"stage-0"โปรดพิจารณาว่าคุณจะต้องตั้งไว้
ในการติดตั้ง:

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

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

อัปเดต:

ใช้อยู่ในปัจจุบัน stage-3


22
ปัญหาคือค่าคงที่สามารถกำหนดใหม่ได้ Op ไม่ต้องการอย่างนั้น
CodingIntrigue

3
FYI ตอนนี้อยู่ใน Babelstage-2
bmaupin

4
นั่นไม่ใช่ค่าคงที่
Dave L.

1
@CodingIntrigue จะเรียกร้องObject.freeze()ให้ชั้นเรียนแก้ไขได้หรือไม่?
พลวง

1
@ พลวงฉันยังไม่ได้ทดสอบ แต่ฉันคิดอย่างนั้น ปัญหาคือจะใช้กับคุณสมบัติทั้งหมดของคลาส ไม่คงที่ด้วย
CodingIntrigue

14

ในเอกสารนี้ระบุว่า:

ไม่มี (โดยเจตนา) ไม่มีวิธีการประกาศโดยตรงในการกำหนดคุณสมบัติของข้อมูลต้นแบบ (นอกเหนือจากวิธีการ) คุณสมบัติคลาสหรือคุณสมบัติของอินสแตนซ์

ก็หมายความว่าจงใจเช่นนี้

บางทีคุณอาจกำหนดตัวแปรในตัวสร้างได้?

constructor(){
    this.key = value
}

2
ใช่มันสามารถทำงานได้ นอกจากนี้ฉันต้องการพูดถึงตัวสร้างที่เรียกใช้เมื่อสร้างอินสแตนซ์และสำหรับแต่ละอินสแตนซ์นี้คีย์จะไม่เหมือนกัน วิธีการและคุณสมบัติคงที่ช่วยให้เราใช้งานได้โดยตรงจากคลาสโดยไม่ต้องสร้างอินสแตนซ์ มีจุดด้อยของวิธีการ / คุณสมบัติแบบคงที่
Kirill Gusyatin

1
ค่าคงที่ควรไม่เปลี่ยนรูป การกำหนดคุณสมบัติให้กับวัตถุในระหว่างการก่อสร้างจะให้คุณสมบัติที่สามารถแก้ไขได้
philraj

12

นอกจากนี้ยังสามารถใช้Object.freezeกับอ็อบเจ็กต์ class (es6) / constructor function (es5) ของคุณเพื่อทำให้ไม่เปลี่ยนรูปได้:

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

การพยายามปรับเปลี่ยนคลาสจะทำให้คุณล้มเหลวอย่างนุ่มนวล (จะไม่ทำให้เกิดข้อผิดพลาดใด ๆ แต่จะไม่มีผลใด ๆ )


3
soft-fail นั้นค่อนข้างน่ากลัวสำหรับพวกเราที่มาจากภาษาอื่น - เพียงแค่ปรับตัวให้เข้ากับแนวคิดที่ว่าเครื่องมือไม่ได้ช่วยเรามากนักในการค้นหาข้อผิดพลาดตอนนี้แม้แต่รันไทม์ก็ยังไม่ช่วย (อย่างอื่นฉันชอบวิธีแก้ปัญหาของคุณ)
ทอม

1
ฉันชอบที่จะObject.freeze()บังคับให้ไม่เปลี่ยนรูปและได้ใช้มันบ่อยมากในช่วงนี้ อย่าลืมใช้ซ้ำ!
jeffwtribble

8

อาจแค่ใส่ค่าคงที่ทั้งหมดของคุณในวัตถุที่ถูกแช่แข็ง?

class MyClass {

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

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

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

1
ฟังก์ชันคงที่ไม่สามารถใช้ตัวแปร "this" ได้
PokerFace

4

เช่นเดียวกับhttps://stackoverflow.com/users/2784136/rodrigo-bottiObject.freeze()กล่าวว่าฉันคิดว่าคุณกำลังมองหา นี่คือตัวอย่างของคลาสที่มีสถิติไม่เปลี่ยนรูป:

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

3

นี่เป็นอีกหนึ่งวิธีที่คุณสามารถทำได้

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

หมายเหตุ - คำสั่งมีความสำคัญคุณไม่สามารถมีค่าคงที่ด้านบนได้

การใช้งาน

console.log(Auto.CONSTANT1);

5
แม้ว่าพวกเขาจะไม่เปลี่ยนรูป
John Harding

2

คุณสามารถสร้างวิธีกำหนดค่าคงที่บนคลาสโดยใช้คุณสมบัติแปลก ๆ ของคลาส ES6 เนื่องจากสถิตยศาสตร์สืบทอดมาจากคลาสย่อยของพวกเขาคุณสามารถทำสิ่งต่อไปนี้:

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

2

คุณสามารถทำให้ "ค่าคงที่" เป็นแบบอ่านอย่างเดียว (ไม่เปลี่ยนรูป) โดยการตรึงคลาส เช่น

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

1

ฉันทำอย่างนี้.

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

ค่าของ PI ได้รับการป้องกันไม่ให้เปลี่ยนแปลงเนื่องจากเป็นค่าที่ส่งคืนจากฟังก์ชัน คุณสามารถเข้าถึงได้ผ่าน Circle.PI ความพยายามใด ๆ ที่จะกำหนดให้ทิ้งลงบนพื้นในลักษณะที่คล้ายกับความพยายามที่จะกำหนดให้กับอักขระสตริงผ่านทาง []


0

หากคุณพอใจที่จะผสมและจับคู่ระหว่างไวยากรณ์ของฟังก์ชันและคลาสคุณสามารถประกาศค่าคงที่หลังคลาสได้ (ค่าคงที่ถูก 'ยก') โปรดทราบว่า Visual Studio Code จะพยายามจัดรูปแบบไวยากรณ์แบบผสมโดยอัตโนมัติ (แม้ว่าจะใช้งานได้)

class MyClass {
    // ...

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


0

คุณสามารถกำหนดได้ดังนี้:

class Foo {
  static MyConst = 200;

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

0

คุณสามารถใช้import * asไวยากรณ์ แม้ว่าจะไม่ใช่คลาส แต่ก็เป็นconstตัวแปรที่แท้จริง

ค่าคงที่ js

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

index.js

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

0

การเพิ่มคำตอบอื่น ๆ คุณต้องส่งออกคลาสเพื่อใช้ในคลาสอื่น นี่คือเวอร์ชัน typescript ของมัน

//Constants.tsx
const DEBUG: boolean = true;

export class Constants {
  static get DEBUG(): boolean {
    return DEBUG;
  }
}

//Anotherclass.tsx
import { Constants } from "Constants";

if (Constants.DEBUG) {
  console.log("debug mode")
}

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.