ค้นหาเว็บไซต์

อีนัมในจาวาสคริปต์กับ ES6


ฉันกำลังสร้างโปรเจกต์ Java เก่าใหม่ใน Javascript และตระหนักว่าไม่มีวิธีที่ดีในการทำ enums ใน JS

สิ่งที่ดีที่สุดที่ฉันสามารถคิดได้คือ:

const Colors = {
    RED: Symbol("red"),
    BLUE: Symbol("blue"),
    GREEN: Symbol("green")
};
Object.freeze(Colors);

const ทำให้ Colors ไม่สามารถถูกกำหนดค่าใหม่ได้ และการทำให้คงที่ช่วยป้องกันการเปลี่ยนแปลงคีย์และค่าของมัน ผมกำลังใช้ Symbols เพื่อให้ Colors.RED ไม่เท่ากับ 0 หรือสิ่งอื่นใดนอกจากตัวมันเอง

มีปัญหากับสูตรนี้หรือไม่? มีวิธีที่ดีกว่านี้หรือเปล่า?


(ฉันรู้ว่าคำถามนี้ซ้ำเล็กน้อย แต่คำถาม/คำตอบก่อนหน้านี้ค่อนข้างเก่า และ ES6 มอบความสามารถใหม่ๆ ให้กับเรา)


แก้ไข:

อีกวิธีแก้ปัญหา ซึ่งเกี่ยวข้องกับปัญหาการทำลำดับ แต่ผมเชื่อว่ายังคงมีปัญหาเรื่องขอบเขต:

const enumValue = (name) => Object.freeze({toString: () => name});

const Colors = Object.freeze({
    RED: enumValue("Colors.RED"),
    BLUE: enumValue("Colors.BLUE"),
    GREEN: enumValue("Colors.GREEN")
});

โดยการใช้การอ้างอิงวัตถุเป็นค่า คุณจะได้รับการหลีกเลี่ยงความขัดแย้งเหมือนกับสัญลักษณ์

คำตอบ #1

มีปัญหากับการจัดสูตรนี้ไหม?

ฉันไม่เห็นเลย

มีวิธีที่ดีกว่านี้ไหม?

ฉันจะรวมสองประโยคให้เป็นประโยคเดียว:

const Colors = Object.freeze({
    RED:   Symbol("red"),
    BLUE:  Symbol("blue"),
    GREEN: Symbol("green")
});

ถ้าคุณไม่ชอบโค้ดซ้ำๆ เช่น การเรียกใช้ Symbol ซ้ำๆ คุณก็สามารถสร้างฟังก์ชันช่วย makeEnum ที่สร้างสิ่งเดียวกันจากรายการชื่อได้เช่นกัน

คำตอบ #2

แม้ว่าการใช้ Symbol เป็นค่าของ enum จะทำงานได้ดีสำหรับกรณีการใช้งานง่าย ๆ แต่การให้คุณสมบัติต่าง ๆ กับ enum ก็สามารถเป็นประโยชน์ได้ ซึ่งสามารถทำได้โดยการใช้ Object เป็นค่าของ enum ที่มีคุณสมบัติเหล่านั้น

ตัวอย่างเช่น เราสามารถให้แต่ละ Colors มีชื่อและค่าเฮ็กซ์ได้:

/**
 * Enum for common colors.
 * @readonly
 * @enum {{name: string, hex: string}}
 */
const Colors = Object.freeze({
  RED:   { name: "red", hex: "#f00" },
  BLUE:  { name: "blue", hex: "#00f" },
  GREEN: { name: "green", hex: "#0f0" }
});

การรวมคุณสมบัติใน enum จะช่วยหลีกเลี่ยงการต้องเขียนคำสั่ง switch (และอาจลืมกรณีใหม่ๆ ในคำสั่ง switch เมื่อมีการขยาย enum) ตัวอย่างนี้ยังแสดงคุณสมบัติและประเภทของ enum ที่ถูกจัดทำเอกสารด้วยคำอธิบายประกอบ JSDoc ของ enum ด้วย

ความเท่าเทียมทำงานตามที่คาดไว้โดย Colors.RED === Colors.RED เป็น true และ Colors.RED === Colors.BLUE เป็น false.

คำตอบ #3

อัปเดต 11.05.2020:
แก้ไขเพื่อรวมฟิลด์และเมธอดสแตติกเพื่อให้ใกล้เคียงกับพฤติกรรมของ enum 'จริง'

ถ้าคุณวางแผนที่จะอัปเดต ผมแนะนำให้ลองใช้สิ่งที่ผมเรียกว่า "Enum Class" (เว้นแต่จะมีข้อจำกัดของเบราว์เซอร์หรือสภาพแวดล้อมรันไทม์ที่คุณไม่สามารถยอมรับได้) มันเป็นคลาสที่ ง่ายมาก และสะอาดตาโดยใช้ฟิลด์แบบส่วนตัวและตัวเข้าถึงที่จำกัดเพื่อจำลองพฤติกรรมของ enum นี่คือสิ่งที่ผมบางครั้งทำใน C# เมื่อผมต้องการสร้างฟังก์ชันเพิ่มเติมให้กับ enum

ฉันตระหนักว่าฟิลด์คลาสส่วนตัวยังอยู่ในช่วงทดลองในตอนนี้ แต่ดูเหมือนว่าจะใช้ได้สำหรับการสร้างคลาสที่มีฟิลด์/คุณสมบัติที่ไม่สามารถเปลี่ยนแปลงได้ การสนับสนุนเบราว์เซรก็พอใช้ได้ เบราว์เซอร์ "หลัก" เพียงตัวเดียวที่ยังไม่รองรับคือ Firefox (ซึ่งฉันมั่นใจว่าพวกเขาจะรองรับในเร็วๆ นี้) และ IE (ไม่สำคัญ)

ข้อจำกัดความรับผิดชอบ:
ผมไม่ใช่นักพัฒนา ผมเพียงแค่รวบรวมสิ่งนี้ขึ้นมาเพื่อแก้ไขข้อจำกัดของการไม่มี enums ใน JS เมื่อผมทำงานในโครงการส่วนตัว

ตัวอย่างชั้นเรียน :

class Colors {
    // Private Fields
    static #_RED = 0;
    static #_GREEN = 1;
    static #_BLUE = 2;

    // Accessors for "get" functions only (no "set" functions)
    static get RED() { return this.#_RED; }
    static get GREEN() { return this.#_GREEN; }
    static get BLUE() { return this.#_BLUE; }
}

ตอนนี้คุณควรจะสามารถเรียกใช้เอนัมของคุณโดยตรงได้แล้ว

Colors.RED; // 0
Colors.GREEN; // 1
Colors.BLUE; // 2

การรวมกันของการใช้ฟิลด์ส่วนตัวและตัวเข้าถึงที่จำกัดหมายความว่าค่าของ enum ที่มีอยู่ได้รับการป้องกันอย่างดี (พวกมันโดยหลักแล้วเป็นค่าคงที่ในตอนนี้)

Colors.RED = 10 // Colors.RED is still 0
Colors._RED = 10 // Colors.RED is still 0
Colors.#_RED = 10 // Colors.RED is still 0

คำตอบ #4

ดังที่กล่าวไว้ด้านบน คุณยังสามารถเขียนฟังก์ชันช่วยเหลือ makeEnum() ได้อีกด้วย:

function makeEnum(arr){
    let obj = Object.create(null);
    for (let val of arr){
        obj[val] = Symbol(val);
    }
    return Object.freeze(obj);
}

ใช้มันแบบนี้:

const Colors = makeEnum(["red","green","blue"]);
let startColor = Colors.red; 
console.log(startColor); // Symbol(red)

if(startColor == Colors.red){
    console.log("Do red things");
}else{
    console.log("Do non-red things");
}

คำตอบ #5

นี่คือวิธีการส่วนตัวของฉัน

class ColorType {
    static get RED () {
        return "red";
    }

    static get GREEN () {
        return "green";
    }

    static get BLUE () {
        return "blue";
    }
}

// Use case.
const color = Color.create(ColorType.RED);

บทความที่เกี่ยวข้อง