ไวยากรณ์ที่ต้องการสำหรับกำหนด enums ใน JavaScript คืออะไร?


2082

ไวยากรณ์ที่ต้องการสำหรับกำหนด enums ใน JavaScript คืออะไร? สิ่งที่ต้องการ:

my.namespace.ColorEnum = {
    RED : 0,
    GREEN : 1,
    BLUE : 2
}

// later on

if(currentColor == my.namespace.ColorEnum.RED) {
   // whatever
}

หรือมีสำนวนที่เป็นที่นิยมมากขึ้น?


131
อย่าใช้0เป็นหมายเลขการแจงนับ นอกเสียจากว่าจะใช้สำหรับบางสิ่งที่ยังไม่ได้ตั้งค่า JS ถือว่าทั้งหมดเป็นค่าเดียวกันเมื่อเทียบกับการใช้false || undefined || null || 0 || "" || '' || NaN ==
matsko

152
@matsko ไม่ได้เป็นเพียงข้อโต้แย้งในการใช้ ==?
sdm350

6
0 == nullส่งคืนค่าเท็จ
mcont

11
แต่false == 0และ+null == 0(และการแปลงไปยังหมายเลขที่เกิดขึ้นบางครั้งเมื่อคุณไม่ได้คาดหวังว่ามัน) ในขณะที่null == undefinedมากเกินไปและ+undefinedเป็นNaN(แม้NaN != NaN)
sanderd17

46
ความเสมอภาคเมทริกซ์คู่มีมากขึ้นทำให้เกิดความสับสนกว่าอัตโนมัติการจัดรูปแบบของ Microsoft word
aaaaaa

คำตอบ:


895

ตั้งแต่ 1.8.5 เป็นไปได้ที่จะผนึกและตรึงวัตถุดังนั้นให้นิยามดังกล่าวข้างต้นดังนี้:

const DaysEnum = Object.freeze({"monday":1, "tuesday":2, "wednesday":3, ...})

หรือ

const DaysEnum = {"monday":1, "tuesday":2, "wednesday":3, ...}
Object.freeze(DaysEnum)

และ voila! JS enums

อย่างไรก็ตามสิ่งนี้ไม่ได้ป้องกันคุณจากการกำหนดค่าที่ไม่พึงประสงค์ให้กับตัวแปรซึ่งมักจะเป็นเป้าหมายหลักของ enums:

let day = DaysEnum.tuesday
day = 298832342 // goes through without any errors

วิธีหนึ่งที่จะให้แน่ใจว่าการศึกษาระดับปริญญาที่แข็งแกร่งของความปลอดภัยประเภท (กับ enums หรืออื่น ๆ ) คือการใช้เครื่องมือเช่นtypescriptหรือไหล

แหล่ง

ไม่จำเป็นต้องใช้เครื่องหมายคำพูด แต่ฉันเก็บไว้เพื่อความสอดคล้อง


6
ตาม Wikipedia ( en.wikipedia.org/wiki/JavaScript#Versions ) มันใช้ได้กับ Firefox 4, IE 9, Opera 11.60 และฉันรู้ว่ามันใช้งานได้ใน Chrome
Artur Czajka

77
นี่คือคำตอบในขณะนี้ในปี 2012 var DaysEnum = Object.freeze ({ monday: {}, tuesday: {}, ... });ที่เรียบง่ายเพิ่มเติม: คุณไม่จำเป็นต้องระบุ id คุณสามารถใช้วัตถุเปล่าเพื่อเปรียบเทียบ enums if (incommingEnum === DaysEnum.monday) //incommingEnum is monday
Gabriel Llamas

34
เพื่อความเข้ากันได้แบบย้อนหลังif (Object.freeze) { Object.freeze(DaysEnum); }
saluce

17
ฉันต้องการจะชี้ให้เห็นว่าการทำ({ monday: {}, ฯลฯ หมายความว่าถ้าคุณแปลงวัตถุนั้นเป็น JSON ด้วยการทำให้เป็นสตริงคุณจะได้[{"day": {}}]สิ่งที่ไม่ได้ผล
jcollum

10
@Supuhstar ความคิดเห็นของฉันเกี่ยวกับคำถามนี้ตอนนี้แตกต่างกัน อย่าใช้ freeze () มันไร้ประโยชน์อย่างสมบูรณ์และเสียเวลาในการทำสิ่งที่ "โง่" หากคุณต้องการที่จะเปิดเผย enum var DaysEnum = {"monday":1, "tuesday":2, "wednesday":3, ...}เพียงแค่เปิดเผยนี้: การเปรียบเทียบออบเจ็กต์ในความคิดเห็นก่อนหน้าของฉันนั้นช้ากว่าการเปรียบเทียบตัวเลข
Gabriel Llamas

608

นี่ไม่ใช่คำตอบที่มากนัก แต่ฉันจะบอกว่ามันใช้ได้ดีส่วนตัว

ต้องบอกว่าเนื่องจากไม่สำคัญว่าค่าคืออะไร (คุณใช้ 0, 1, 2) ฉันจะใช้สตริงที่มีความหมายในกรณีที่คุณต้องการส่งออกค่าปัจจุบัน


377
นี่เป็นอีกคำตอบหนึ่ง แต่เนื่องจากคำตอบนี้เป็นคำตอบที่ยอมรับฉันจะโพสต์ที่นี่ ทางออกของ OP นั้นถูกต้อง มันจะดียิ่งขึ้น Object.freeze()แต่ถ้าใช้กับ สิ่งนี้จะป้องกันไม่ให้โค้ดอื่นเปลี่ยนค่าของ enum ตัวอย่าง:var ColorEnum = Object.freeze({RED: 0, GREEN: 1, BLUE: 2});
Sildoreth

5
@TolgaE ขอบคุณสำหรับห้องสมุด! มันเป็นแรงบันดาลใจให้ฉันไม่เพียง แต่ต้มให้น้อยที่สุด แต่ยังเพิ่มฟีเจอร์สองสามอย่าง! ฉันขอแยกคุณและวางไว้ที่นี่: github.com/BlueHuskyStudios/Micro-JS-Enum
Supuhstar

3
@Supuhstar เยี่ยมมาก! ฉันดีใจที่คุณสามารถใช้มัน .. รู้สึกฟรีเพื่อดึงคำขอหากคุณต้องการให้มันรวมอยู่ในห้องสมุดนี้จากนั้นฉันสามารถอัปเดตห้องสมุด npm
Tolga E

2
หากใครมีความสนใจฉันได้ใช้ Enum แบบปลอดภัยคล้ายกับพวกเขาใน Java ซึ่งหมายความว่าคุณสามารถinstanceofตรวจสอบได้ ตัวอย่างเช่นColorEnum.RED instanceof ColorEnum(ส่งคืนtrue) นอกจากนี้คุณยังสามารถแก้ไขอินสแตนซ์จากชื่อColorEnum.fromName("RED") === ColorEnum.RED(ส่งคืนtrue) แต่ละอินสแตนซ์ยังมี a .name()และ.ordinal()เมธอดและ enum เองมีvalues()เมธอดที่ส่งกลับอาร์เรย์ของค่าคงที่ทั้งหมด
Vivin Paliath

3
ฉันไม่แน่ใจว่าฉันเห็นด้วยกับคำแนะนำ "สตริงที่มีความหมาย" Enums ไม่ควรถูกมองว่าเป็นสตริงหรือตัวเลข เป็นชนิดข้อมูลนามธรรม ไม่ควร "เอาท์พุทค่าปัจจุบัน" โดยไม่ใช้วิธีตัวช่วย ใน Java และ. NET เป็นToString()วิธีการ พวกเรา JS devs นั้นพึ่งพาวิธีต่าง ๆ เช่น "ทำงานได้ดี" อยู่แล้ว! นอกจากนี้หนึ่งควรจะสามารถอย่างรวดเร็วswitchใน enum การเปรียบเทียบสตริงช้ากว่าการเปรียบเทียบตัวเลขดังนั้นคุณจะได้รับswitchประสิทธิภาพที่แย่ลงเล็กน้อยหากคุณใช้สตริงแทนจำนวนเต็ม
Rabadash8820

501

UPDATE

ขอบคุณสำหรับ upvotes ทุกคน แต่ฉันไม่คิดว่าคำตอบของฉันด้านล่างเป็นวิธีที่ดีที่สุดในการเขียน enums ใน JavaScript อีกต่อไป ดูโพสต์บล็อกของฉันสำหรับรายละเอียดเพิ่มเติม: Enums ใน JavaScript


การแจ้งเตือนชื่อเป็นไปได้แล้ว:

if (currentColor == my.namespace.ColorEnum.RED) {
   // alert name of currentColor (RED: 0)
   var col = my.namespace.ColorEnum;
   for (var name in col) {
     if (col[name] == col.RED)
       alert(name);
   }
}

อีกทางหนึ่งคุณสามารถสร้างวัตถุค่าเพื่อให้คุณมีเค้กและกินมันเกินไป:

var SIZE = {
  SMALL : {value: 0, name: "Small", code: "S"}, 
  MEDIUM: {value: 1, name: "Medium", code: "M"}, 
  LARGE : {value: 2, name: "Large", code: "L"}
};

var currentSize = SIZE.MEDIUM;
if (currentSize == SIZE.MEDIUM) {
  // this alerts: "1: Medium"
  alert(currentSize.value + ": " + currentSize.name);
}

ใน JavaScript เนื่องจากเป็นภาษาไดนามิกจึงเป็นไปได้ที่จะเพิ่มค่า enum ให้กับชุดในภายหลัง:

// Add EXTRALARGE size
SIZE.EXTRALARGE = {value: 3, name: "Extra Large", code: "XL"};

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

// Add 'Extra Large' size, only knowing it's name
var name = "Extra Large";
SIZE[name] = {value: -1, name: name, code: "?"};

แน่นอนนี่หมายความว่าไม่สามารถทำการตั้งสมมติฐานบางอย่างได้อีกต่อไป (ค่าดังกล่าวหมายถึงลำดับที่ถูกต้องสำหรับขนาดตัวอย่าง)

โปรดจำไว้ว่าใน JavaScript วัตถุเช่นเดียวกับแผนที่หรือตารางแฮช ชุดของคู่ค่าชื่อ คุณสามารถวนซ้ำพวกเขาหรือจัดการพวกเขาโดยไม่ต้องรู้ล่วงหน้าเกี่ยวกับพวกเขามากนัก

ตัวอย่าง

for (var sz in SIZE) {
  // sz will be the names of the objects in SIZE, so
  // 'SMALL', 'MEDIUM', 'LARGE', 'EXTRALARGE'
  var size = SIZE[sz]; // Get the object mapped to the name in sz
  for (var prop in size) {
    // Get all the properties of the size object, iterates over
    // 'value', 'name' and 'code'. You can inspect everything this way.        
  }
} 

และถ้าคุณสนใจเนมสเปซคุณอาจต้องการดูโซลูชันของฉันสำหรับเนมสเปซที่เรียบง่าย แต่ทรงพลังและการจัดการการพึ่งพาสำหรับ JavaScript: แพ็คเกจ JS


งั้นคุณจะสร้าง SIZE ได้อย่างไรถ้าคุณมีเพียงชื่อเท่านั้น
Johanisma

2
@Johanisma: กรณีการใช้งานนั้นไม่ได้ทำให้รู้สึกจริงสำหรับ enums เพราะความคิดทั้งหมดของพวกเขาคือการที่คุณรู้คุณค่าทั้งหมดล่วงหน้า อย่างไรก็ตามไม่มีอะไรหยุดคุณจากการเพิ่มค่าพิเศษในภายหลังใน Javascript ฉันจะเพิ่มตัวอย่างของคำตอบนั้น
Stijn de Witt

2
+1 สำหรับลิงก์ไปยังโพสต์ของคุณด้วยวิธีการของคุณสมบัติ สง่างามในการประกาศขั้นพื้นฐานง่าย ๆ เช่นเดียวกับใน OP พร้อมคุณสมบัติเพิ่มเมื่อต้องการ
goodeye

@Stijin ชอบโซลูชันที่อัปเดตของคุณจริงๆ โพสต์รหัสในความคิดเห็นในบล็อกของคุณและเป็นความคิดเห็นด้านล่าง โดยพื้นฐานแล้วการใช้ฟังก์ชั่นดำเนินการสร้างคุณสมบัติจากรายการแฮชที่มีอยู่และเลือกที่จะหยุดมัน (mkenum_2 ในรายการของฉัน) ไชโย
Andrew Philips

นอกจากนี้ยังมีห้องสมุดที่ใช้งานรวมถึงคุณสมบัติที่ดีในการเปรียบเทียบและค้นหาแบบกลับรายการ: github.com/adrai/enum
Roman M

83

บรรทัดล่าง: คุณไม่สามารถ

คุณสามารถปลอมมัน แต่คุณจะไม่ได้รับความปลอดภัยประเภท โดยทั่วไปจะทำโดยการสร้างพจนานุกรมอย่างง่าย ๆ ของค่าสตริงที่แมปกับค่าจำนวนเต็ม ตัวอย่างเช่น:

var DaysEnum = {"monday":1, "tuesday":2, "wednesday":3, ...}

Document.Write("Enumerant: " + DaysEnum.tuesday);

มีปัญหากับวิธีนี้หรือไม่? คุณสามารถกำหนดซ้ำตัวนับของคุณโดยไม่ตั้งใจหรือตั้งใจมีค่าซ้ำกันโดยไม่ตั้งใจ ตัวอย่างเช่น:

DaysEnum.monday = 4; // whoops, monday is now thursday, too

แก้ไข

แล้ว Object.freeze ของ Artur Czajka ล่ะ? จะไม่ทำงานเพื่อป้องกันคุณจากการตั้งวันจันทร์ถึงวันพฤหัสบดี? - ทอด Quad

แน่นอนว่าObject.freezeทั้งหมดจะแก้ไขปัญหาที่ฉันบ่นเกี่ยวกับ ฉันอยากจะเตือนทุกคนว่าเมื่อฉันเขียนข้างต้นObject.freezeไม่มีอยู่จริง

ตอนนี้ .... ตอนนี้มันเปิดโอกาสที่น่าสนใจมากขึ้น

แก้ไข 2
นี่คือห้องสมุดที่ดีมากสำหรับการสร้าง enums

http://www.2ality.com/2011/10/enums.html

แม้ว่ามันอาจจะไม่เหมาะกับการใช้ enums ทุกอย่าง แต่มันไปไกลมาก


103
ประเภทความปลอดภัยในจาวาสคริปต์คืออะไร?
Scott Evernden

3
ดังนั้นอย่าจับคู่ค่ากับคุณสมบัติของวัตถุ ใช้ทะเยอทะยานในการเข้าถึงตัวนับ (เก็บไว้เป็นทรัพย์สินของพูด "ส่วนตัว" วัตถุ) การดำเนินไร้เดียงสาจะมีลักษณะ -var daysEnum = (function(){ var daysEnum = { monday: 1, tuesday: 2 }; return { get: function(value){ return daysEnum[value]; } } })(); daysEnum.get('monday'); // 1
kangax

2
@Scott Evernden: จุดยึด @ Kangax: ประเด็นก็คือว่ามันยังคงแฮ็ก Enums ไม่มีอยู่ใน Javascript, ช่วงเวลา, จุดสิ้นสุดของเรื่องราว แม้แต่รูปแบบที่แนะนำโดย Tim Sylvester ก็ยังเป็นแฮ็กที่เหมาะ
Randolpho

2
การโรยโค้ดด้วยตัวอักษรไม่สามารถบำรุงรักษาได้มากดังนั้นจึงเหมาะสมที่จะสร้างค่าคงที่ แน่นอนว่า Javascript ไม่มีค่าคงที่เช่นกัน ดังนั้นโดยทั่วไปนี่เป็นเพียงวิธีเขียนโค้ดที่สะอาด ไม่สามารถบังคับใช้ได้ แต่มีไม่มากใน Javascript คุณสามารถกำหนดค่าคงที่หรือฟังก์ชั่นหรือส่วนใหญ่เป็นอะไรก็ได้ EG: document.getElementById = function () {alert ("คุณเมาแล้วจาวาสคริปต์ไม่ใช่ประเภทที่ปลอดภัย");};
Stijn de Witt

3
@ Randolpho: แล้ว Object.freeze ของ Artur Czajka ล่ะ? จะไม่ทำงานเพื่อป้องกันคุณจากการตั้งวันจันทร์ถึงวันพฤหัสบดี?
ไมเคิล - Clay Shirky

56

นี่คือสิ่งที่เราทุกคนต้องการ:

function Enum(constantsList) {
    for (var i in constantsList) {
        this[constantsList[i]] = i;
    }
}

ตอนนี้คุณสามารถสร้าง enums ของคุณ:

var YesNo = new Enum(['NO', 'YES']);
var Color = new Enum(['RED', 'GREEN', 'BLUE']);

โดยทำสิ่งนี้ค่าคงที่สามารถ acessed ในวิธีปกติ (YesNo.YES, Color.GREEN) และพวกเขาได้รับค่า int ตามลำดับ (NO = 0, YES = 1; RED = 0, GRE = 1, BLUE = 2)

คุณยังสามารถเพิ่มวิธีการโดยใช้ Enum.prototype:

Enum.prototype.values = function() {
    return this.allValues;
    /* for the above to work, you'd need to do
            this.allValues = constantsList at the constructor */
};


แก้ไข - ปรับปรุงเล็กน้อย - ตอนนี้มี varargs: (น่าเสียดายที่มันไม่ทำงานอย่างถูกต้องบน IE: S ... ควรติดกับเวอร์ชันก่อนหน้าแล้ว)

function Enum() {
    for (var i in arguments) {
        this[arguments[i]] = i;
    }
}

var YesNo = new Enum('NO', 'YES');
var Color = new Enum('RED', 'GREEN', 'BLUE');

รักความเรียบง่ายของคำตอบนี้!
Marquizzo

@Marquizzo (และ OP) ฉันสร้างรุ่นที่ปรับปรุงแล้วตามคำตอบนี้: stackoverflow.com/a/60309416/1599699
Andrew

53

ในเบราว์เซอร์ที่ทันสมัยส่วนใหญ่จะมีสัญลักษณ์ชนิดข้อมูลดั้งเดิมที่สามารถใช้ในการสร้างการแจงนับ มันจะมั่นใจในความปลอดภัยประเภทของ enum เป็นสัญลักษณ์แต่ละค่าค้ำประกันโดย JavaScript Symbol() != Symbol()จะไม่ซ้ำกันคือ ตัวอย่างเช่น:

const COLOR = Object.freeze({RED: Symbol(), BLUE: Symbol()});

ในการทำให้การดีบักง่ายขึ้นคุณสามารถเพิ่มคำอธิบายลงในค่า enum:

const COLOR = Object.freeze({RED: Symbol("RED"), BLUE: Symbol("BLUE")});

พลั่วสาธิต

บนGitHubคุณสามารถค้นหา wrapper ที่ลดความซับซ้อนของรหัสที่จำเป็นในการเริ่มต้น enum:

const color = new Enum("RED", "BLUE")

color.RED.toString() // Symbol(RED)
color.getName(color.RED) // RED
color.size // 2
color.values() // Symbol(RED), Symbol(BLUE)
color.toString() // RED,BLUE

นี่คือคำตอบที่ถูกต้องในทางทฤษฎี ในทางปฏิบัติการสนับสนุนเบราว์เซอร์ปี 2558 ยังไม่เพียงพอ ยังไม่พร้อมผลิต
vbraun

1
แม้ว่าการสนับสนุนเบราว์เซอร์จะไม่ได้มี แต่นี่เป็นคำตอบที่ดีที่สุดเพราะใกล้เคียงกับที่Symbolตั้งใจไว้
rvighne

2
Meh ... ค่า enum มักจะต้องสามารถทำให้เป็นอนุกรมได้ แต่สัญลักษณ์ไม่สะดวกในการทำให้เป็นอนุกรมและทำให้เป็น deserialize
Andy

3
มันเป็นแค่ฉันหรือเป็นObject.freezeเพียงสำหรับคนที่ไม่ยอมรับความจริงที่ว่า "monkeypatch ที่มีความเสี่ยงของคุณเอง" เป็นสัญญาทางสังคมของ JS?
Andy

@Andy ใช่เป็นอันดับเป็นที่น่ารำคาญ ฉันลงเอยอย่างชัดเจนtoJSONในคลาสที่มีอยู่เพื่อใช้วิธีนี้: stackoverflow.com/questions/58499828/ …
Ciro Santilli 冠状病毒审查审查六四事件法轮功

30

𝗣𝗹𝗮𝗶𝗻𝗩𝗮𝗻𝗶𝗹𝗹𝗮𝗝𝗦𝗩𝗮𝗿𝗶𝗮𝗯𝗹𝗲𝗡𝗮𝗺𝗲𝘀

ขอให้เราตัดปัญหาขนาดไฟล์ได้โดยตรง คำตอบอื่น ๆ ที่แสดงไว้ที่นี่จะทำให้โค้ดของคุณเต็ม ฉันขอเสนอให้คุณทราบว่าเพื่อประสิทธิภาพที่ดีที่สุดความสามารถในการอ่านโค้ดการจัดการโครงการขนาดใหญ่ไวยากรณ์ที่บอกใบ้ในโปรแกรมแก้ไขโค้ดจำนวนมากและการลดขนาดรหัสโดยการทำให้เป็น minification นี่เป็นวิธีที่ถูกต้องในการระบุ


wvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwvwv

Underscore-Notation Variables

ดังที่แสดงในแผนภูมิด้านบนและตัวอย่างด้านล่างนี่คือห้าขั้นตอนง่าย ๆ ในการเริ่มต้น:

  1. กำหนดชื่อสำหรับกลุ่มการแจงนับ คิดว่าเป็นคำนามที่สามารถอธิบายวัตถุประสงค์ของการแจงนับหรืออย่างน้อยรายการในการแจงนับ ตัวอย่างเช่นกลุ่มของการแจกแจงที่แสดงถึงสีที่ผู้ใช้สามารถเลือกได้อาจมีชื่อว่า COLORCHOICES ได้ดีกว่า COLORS
  2. ตัดสินใจว่าการแจกแจงในกลุ่มเป็นแบบเอกสิทธิ์เฉพาะบุคคลหรือเป็นอิสระ ENUM_ถ้าร่วมกันผูกขาดเริ่มต้นในแต่ละชื่อตัวแปรแจกแจงด้วย INDEX_หากอิสระหรือด้านข้างโดยใช้
  3. สำหรับแต่ละรายการสร้างตัวแปรโลคัลใหม่ที่ชื่อขึ้นต้นด้วยENUM_หรือINDEX_จากนั้นชื่อของกลุ่มจากนั้นขีดล่างจากนั้นเป็นชื่อที่จำง่ายเฉพาะสำหรับคุณสมบัติ
  4. เพิ่มENUMLENGTH_, ENUMLEN_, INDEXLENGTH_หรือINDEXLEN_(ไม่ว่าLEN_หรือLENGTH_เป็นความชอบส่วนบุคคล) ตัวแปรที่ปลายสุดแจกแจง คุณควรใช้ตัวแปรนี้เมื่อใดก็ตามที่เป็นไปได้ในรหัสของคุณเพื่อให้แน่ใจว่าการเพิ่มรายการพิเศษในการแจงนับและการเพิ่มค่านี้จะไม่ทำให้โค้ดของคุณเสียหาย
  5. ให้แต่ละตัวแปรนับเนื่องหนึ่งค่ามากขึ้นกว่าที่ผ่านมาเริ่มต้นที่ 0 มีความคิดเห็นเกี่ยวกับหน้านี้ที่บอกว่ามี0ไม่ควรใช้เป็นค่าแจกแจงเพราะ0 == null, 0 == false, 0 == ""และอื่น ๆ ที่ JS ความบ้าคลั่ง ฉันส่งให้คุณเพื่อหลีกเลี่ยงปัญหานี้และเพิ่มประสิทธิภาพในเวลาเดียวกันให้ใช้===และไม่ให้==ปรากฏในโค้ดของคุณยกเว้นด้วยtypeof(เช่นtypeof X == "string") ในทุกปีของการใช้===ฉันไม่เคยมีปัญหากับการใช้ 0 เป็นค่าการแจงนับครั้งเดียว หากคุณยังคลื่นไส้คุณ1สามารถใช้เป็นค่าเริ่มต้นในการENUM_แจกแจง (แต่ไม่ใช่ในการINDEX_แจกแจง) โดยไม่มีการลงโทษประสิทธิภาพในหลายกรณี
const ENUM_COLORENUM_RED   = 0;
const ENUM_COLORENUM_GREEN = 1;
const ENUM_COLORENUM_BLUE  = 2;
const ENUMLEN_COLORENUM    = 3;

// later on

if(currentColor === ENUM_COLORENUM_RED) {
   // whatever
}

นี่คือวิธีที่ฉันจำได้ว่าควรใช้INDEX_เมื่อใดและเมื่อใดENUM_:

// Precondition: var arr = []; //
arr[INDEX_] = ENUM_;

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

const ENUM_PET_CAT = 0,
      ENUM_PET_DOG = 1,
      ENUM_PET_RAT = 2,
      ENUMLEN_PET  = 3;

var favoritePets = [ENUM_PET_CAT, ENUM_PET_DOG, ENUM_PET_RAT,
                    ENUM_PET_DOG, ENUM_PET_DOG, ENUM_PET_CAT,
                    ENUM_PET_RAT, ENUM_PET_CAT, ENUM_PET_DOG];

var petsFrequency = [];

for (var i=0; i<ENUMLEN_PET; i=i+1|0)
  petsFrequency[i] = 0;

for (var i=0, len=favoritePets.length|0, petId=0; i<len; i=i+1|0)
  petsFrequency[petId = favoritePets[i]|0] = (petsFrequency[petId]|0) + 1|0;

console.log({
    "cat": petsFrequency[ENUM_PET_CAT],
    "dog": petsFrequency[ENUM_PET_DOG],
    "rat": petsFrequency[ENUM_PET_RAT]
});

สังเกตว่าในรหัสข้างต้นมันง่ายที่จะเพิ่มในสัตว์เลี้ยงชนิดใหม่: คุณจะต้องเพิ่มรายการใหม่หลังจากนั้นENUM_PET_RATและอัปเดตENUMLEN_PETตามนั้น มันอาจเป็นเรื่องยากและ buggy เพื่อเพิ่มรายการใหม่ในระบบการแจงนับอื่น ๆ


เว็

𝗘𝘅𝘁𝗲𝗻𝗱𝗨𝗽𝗽𝗲𝗿𝗰𝗮𝘀𝗲𝗩𝗮𝗿𝗶𝗮𝗯𝗹𝗲𝘀𝗪𝗶𝘁𝗵𝗔𝗱𝗱𝗶𝘁𝗶𝗼𝗻

นอกจากนี้ไวยากรณ์ของการแจกแจงนี้ช่วยให้การขยายคลาสที่ชัดเจนและกระชับดังที่เห็นด้านล่าง ในการขยายคลาสให้เพิ่มจำนวนที่เพิ่มขึ้นในLEN_รายการของคลาสพาเรนต์ จากนั้นให้แบ่งคลาสย่อยออกด้วยLEN_รายการของตัวเองเพื่อให้คลาสย่อยอาจขยายออกไปอีกในอนาคต

แผนภาพส่วนขยายเพิ่มเติม

(function(window){
    "use strict";
    var parseInt = window.parseInt;

    // use INDEX_ when representing the index in an array instance
    const INDEX_PIXELCOLOR_TYPE = 0, // is a ENUM_PIXELTYPE
          INDEXLEN_PIXELCOLOR   = 1,
          INDEX_SOLIDCOLOR_R    = INDEXLEN_PIXELCOLOR+0,
          INDEX_SOLIDCOLOR_G    = INDEXLEN_PIXELCOLOR+1,
          INDEX_SOLIDCOLOR_B    = INDEXLEN_PIXELCOLOR+2,
          INDEXLEN_SOLIDCOLOR   = INDEXLEN_PIXELCOLOR+3,
          INDEX_ALPHACOLOR_R    = INDEXLEN_PIXELCOLOR+0,
          INDEX_ALPHACOLOR_G    = INDEXLEN_PIXELCOLOR+1,
          INDEX_ALPHACOLOR_B    = INDEXLEN_PIXELCOLOR+2,
          INDEX_ALPHACOLOR_A    = INDEXLEN_PIXELCOLOR+3,
          INDEXLEN_ALPHACOLOR   = INDEXLEN_PIXELCOLOR+4,
    // use ENUM_ when representing a mutually-exclusive species or type
          ENUM_PIXELTYPE_SOLID = 0,
          ENUM_PIXELTYPE_ALPHA = 1,
          ENUM_PIXELTYPE_UNKNOWN = 2,
          ENUMLEN_PIXELTYPE    = 2;

    function parseHexColor(inputString) {
        var rawstr = inputString.trim().substring(1);
        var result = [];
        if (rawstr.length === 8) {
            result[INDEX_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_ALPHA;
            result[INDEX_ALPHACOLOR_R] = parseInt(rawstr.substring(0,2), 16);
            result[INDEX_ALPHACOLOR_G] = parseInt(rawstr.substring(2,4), 16);
            result[INDEX_ALPHACOLOR_B] = parseInt(rawstr.substring(4,6), 16);
            result[INDEX_ALPHACOLOR_A] = parseInt(rawstr.substring(4,6), 16);
        } else if (rawstr.length === 4) {
            result[INDEX_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_ALPHA;
            result[INDEX_ALPHACOLOR_R] = parseInt(rawstr[0], 16) * 0x11;
            result[INDEX_ALPHACOLOR_G] = parseInt(rawstr[1], 16) * 0x11;
            result[INDEX_ALPHACOLOR_B] = parseInt(rawstr[2], 16) * 0x11;
            result[INDEX_ALPHACOLOR_A] = parseInt(rawstr[3], 16) * 0x11;
        } else if (rawstr.length === 6) {
            result[INDEX_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_SOLID;
            result[INDEX_SOLIDCOLOR_R] = parseInt(rawstr.substring(0,2), 16);
            result[INDEX_SOLIDCOLOR_G] = parseInt(rawstr.substring(2,4), 16);
            result[INDEX_SOLIDCOLOR_B] = parseInt(rawstr.substring(4,6), 16);
        } else if (rawstr.length === 3) {
            result[INDEX_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_SOLID;
            result[INDEX_SOLIDCOLOR_R] = parseInt(rawstr[0], 16) * 0x11;
            result[INDEX_SOLIDCOLOR_G] = parseInt(rawstr[1], 16) * 0x11;
            result[INDEX_SOLIDCOLOR_B] = parseInt(rawstr[2], 16) * 0x11;
        } else {
            result[INDEX_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_UNKNOWN;
        }
        return result;
    }

    // the red component of green
    console.log(parseHexColor("#0f0")[INDEX_SOLIDCOLOR_R]);
    // the alpha of transparent purple
    console.log(parseHexColor("#f0f7")[INDEX_ALPHACOLOR_A]); 
    // the enumerated array for turquoise
    console.log(parseHexColor("#40E0D0"));
})(self);

(ความยาว: 2,450 ไบต์)

บางคนอาจบอกว่านี่เป็นวิธีการที่น้อยกว่าวิธีแก้ปัญหาอื่น ๆ : มันช่วยลดพื้นที่ว่างได้มันใช้เวลานานในการเขียนและมันไม่ได้เคลือบด้วยน้ำตาล คนเหล่านั้นจะถูกต้องหากพวกเขาไม่ย่อหย่อนรหัสของพวกเขา อย่างไรก็ตามไม่มีบุคคลที่เหมาะสมจะทิ้งรหัสที่ไม่ได้ระบุไว้ในผลิตภัณฑ์สุดท้าย สำหรับการลดขนาดนี้ตัวรวบรวมการปิดเป็นสิ่งที่ดีที่สุดที่ฉันยังไม่พบ การเข้าถึงออนไลน์สามารถพบได้ที่นี่ ตัวรวบรวมการปิดสามารถใช้ข้อมูลการแจงนับทั้งหมดและอินไลน์ทำให้ Javascript ของคุณมีขนาดเล็กมากและทำงานได้อย่างรวดเร็วเป็นพิเศษ ดังนั้นลดขนาดด้วยคอมไพเลอร์ปิด สังเกต.


เว็

𝗪𝗶𝘁𝗵𝗠𝗶𝗻𝗶𝗳𝘆 𝗖𝗹𝗼𝘀𝘂𝗿𝗲𝗖𝗼𝗺𝗽𝗶𝗹𝗲𝗿

ผู้เรียบเรียงการปิดสามารถทำการปรับแต่งที่น่าเหลือเชื่อผ่านการอนุมานที่เกินกว่าขีดความสามารถของเครื่องบดย่อย Javascript อื่น ๆ การปิดคอมไพเลอร์สามารถตั้งค่าตัวแปรดั้งเดิมแบบอินไลน์ให้เป็นค่าคงที่ ผู้รวบรวมการคอมไพล์ยังสามารถทำการอนุมานโดยอิงตามค่า inlined เหล่านี้และกำจัดการบล็อกที่ไม่ได้ใช้ในคำสั่ง if และคำสั่งวนซ้ำ

การบิดรหัสผ่านทางตัวรวบรวมการปิด

'use strict';(function(e){function d(a){a=a.trim().substring(1);var b=[];8===a.length?(b[0]=1,b[1]=c(a.substring(0,2),16),b[2]=c(a.substring(2,4),16),b[3]=c(a.substring(4,6),16),b[4]=c(a.substring(4,6),16)):4===a.length?(b[1]=17*c(a[0],16),b[2]=17*c(a[1],16),b[3]=17*c(a[2],16),b[4]=17*c(a[3],16)):6===a.length?(b[0]=0,b[1]=c(a.substring(0,2),16),b[2]=c(a.substring(2,4),16),b[3]=c(a.substring(4,6),16)):3===a.length?(b[0]=0,b[1]=17*c(a[0],16),b[2]=17*c(a[1],16),b[3]=17*c(a[2],16)):b[0]=2;return b}var c=
e.parseInt;console.log(d("#0f0")[1]);console.log(d("#f0f7")[4]);console.log(d("#40E0D0"))})(self);

(ความยาว: 605 ไบต์)

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


เว็

𝗦𝗺𝗮𝗹𝗹𝗲𝗿𝗖𝗼𝗱𝗲𝗦𝗶𝘇𝗲

ตอนนี้ให้เราดูว่าไฟล์ที่เทียบเท่าจะมีขนาดใหญ่เท่าใดหากไม่มีตัวระบุใด ๆ เหล่านี้

แหล่งที่มาโดยไม่ใช้การแจกแจง (ความยาว: 1,973 ไบต์ (สั้นกว่ารหัสที่ระบุ! 477 ไบต์))
ย่อโดยไม่ใช้การแจกแจง (ความยาว: 843 ไบต์ ( ยาวกว่ารหัสการนับ 238 ไบต์))

แผนภูมิขนาดรหัส



เท่าที่เห็นโดยไม่มีการแจกแจงรหัสแหล่งที่มาจะสั้นลงในราคาของรหัส minified ที่มีขนาดใหญ่กว่า ฉันไม่รู้เกี่ยวกับคุณ แต่ฉันรู้แน่นอนว่าฉันไม่ได้รวมซอร์สโค้ดไว้ในผลิตภัณฑ์สุดท้าย ดังนั้นรูปแบบของการแจกแจงนี้จึงเป็นสิ่งที่ไกลเกินกว่าที่จะส่งผลให้มีขนาดไฟล์ที่เล็กลง


เว็

𝗖𝗼𝗼𝗽𝗲𝗿𝗮𝘁𝗶𝘃𝗲🤝𝗕𝘂𝗴𝗙𝗶𝘅𝗶𝗻𝗴

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

// JG = Jack Giffin
const ENUM_JG_COLORENUM_RED   = 0,
      ENUM_JG_COLORENUM_GREEN = 1,
      ENUM_JG_COLORENUM_BLUE  = 2,
      ENUMLEN_JG_COLORENUM    = 3;

// later on

if(currentColor === ENUM_JG_COLORENUM_RED) {
   // whatever
}

// PL = Pepper Loftus
// BK = Bob Knight
const ENUM_PL_ARRAYTYPE_UNSORTED   = 0,
      ENUM_PL_ARRAYTYPE_ISSORTED   = 1,
      ENUM_BK_ARRAYTYPE_CHUNKED    = 2, // added by Bob Knight
      ENUM_JG_ARRAYTYPE_INCOMPLETE = 3, // added by jack giffin
      ENUMLEN_PL_COLORENUM         = 4;

// later on

if(
  randomArray === ENUM_PL_ARRAYTYPE_UNSORTED ||
  randomArray === ENUM_BK_ARRAYTYPE_CHUNKED
) {
   // whatever
}

𝗦𝘂𝗽𝗲𝗿𝗶𝗼𝗿𝗣𝗲𝗿𝗳𝗼𝗿𝗺𝗮𝗻𝗰𝗲

นอกจากนี้รูปแบบของการแจงนับนี้ยังเร็วขึ้นมากหลังจากการลดขนาด ในคุณสมบัติที่มีชื่อปกติเบราว์เซอร์จะต้องใช้แฮชแมพเพื่อค้นหาตำแหน่งที่คุณสมบัติอยู่บนวัตถุ ถึงแม้ว่าคอมไพเลอร์ของ JIT จะเก็บตำแหน่งนี้ไว้บนวัตถุอย่างชาญฉลาด แต่ก็ยังมีค่าใช้จ่ายสูงมากเนื่องจากมีกรณีพิเศษเช่นการลบคุณสมบัติที่ต่ำกว่าจากวัตถุ

แต่ด้วยarrayed PACKED_ELEMENTSจำนวนเต็มดัชนีที่เบาบางเบราว์เซอร์สามารถข้ามค่าใช้จ่ายส่วนใหญ่ได้เนื่องจากดัชนีของค่าในอาร์เรย์ภายในได้ถูกระบุไว้แล้ว ใช่ตามมาตรฐาน ECMAScript คุณสมบัติทั้งหมดควรถือเป็นสตริง อย่างไรก็ตามมาตรฐาน ECMAScript นี้ทำให้เข้าใจผิดเกี่ยวกับประสิทธิภาพเนื่องจากเบราว์เซอร์ทั้งหมดมีการปรับแต่งพิเศษสำหรับดัชนีตัวเลขในอาร์เรย์

/// Hashmaps are slow, even with JIT juice
var ref = {};
ref.count = 10;
ref.value = "foobar";

เปรียบเทียบรหัสด้านบนกับรหัสด้านล่าง

/// Arrays, however, are always lightning fast
const INDEX_REFERENCE_COUNT = 0;
const INDEX_REFERENCE_VALUE = 1;
const INDEXLENGTH_REFERENCE = 2;

var ref = [];
ref[INDEX_REFERENCE_COUNT] = 10;
ref[INDEX_REFERENCE_VALUE] = "foobar";

หนึ่งอาจคัดค้านรหัสที่มีการแจกแจงดูเหมือนจะนานกว่ารหัสที่มีวัตถุธรรมดา แต่รูปลักษณ์ที่สามารถหลอกลวง สิ่งสำคัญคือต้องจำไว้ว่าขนาดซอร์สโค้ดไม่ได้เป็นสัดส่วนกับขนาดเอาต์พุตเมื่อใช้ epic Closure Compiler สังเกต.

/// Hashmaps are slow, even with JIT juice
var a={count:10,value:"foobar"};

โค้ดย่อส่วนที่ไม่มีการแจกแจงอยู่เหนือและโค้ดย่อที่มีการแจงนับอยู่ด้านล่าง

/// Arrays, however, are always lightning fast
var a=[10,"foobar"];

ตัวอย่างข้างต้นแสดงให้เห็นว่านอกเหนือจากการมีประสิทธิภาพที่เหนือกว่าแล้วรหัสที่แจกแจงยังส่งผลให้ขนาดไฟล์เล็กลงด้วย


เว็

𝗘𝗮𝘀𝘆𝗗𝗲𝗯𝘂𝗴𝗴𝗶𝗻𝗴

นอกจากนี้เชอร์รี่ส่วนบุคคลของคนนี้อยู่ด้านบนกำลังใช้รูปแบบของการแจกแจงนี้พร้อมกับโปรแกรมแก้ไขข้อความCodeMirrorในโหมด Javascript โหมดการเน้นไวยากรณ์ของ Javascript ของ CodeMirror ไฮไลต์ตัวแปรท้องถิ่นในขอบเขตปัจจุบัน ด้วยวิธีนี้คุณจะรู้ได้ทันทีเมื่อคุณพิมพ์ชื่อตัวแปรอย่างถูกต้องเพราะหากชื่อตัวแปรถูกประกาศด้วยvarคำสำคัญก่อนหน้านี้แล้วชื่อตัวแปรจะเปลี่ยนเป็นสีพิเศษ (สีฟ้าโดยค่าเริ่มต้น) แม้ว่าคุณจะไม่ได้ใช้ CodeMirror ก็ตามเบราว์เซอร์ก็ยังมีประโยชน์[variable name] is not definedข้อยกเว้นเมื่อรันโค้ดด้วยชื่อการพิมพ์ผิด นอกจากนี้เครื่องมือ JavaScript เช่น JSLint และ Closure Compiler ดังมากเกี่ยวกับการบอกคุณเมื่อคุณพิมพ์ผิดในชื่อตัวแปรการแจงนับ CodeMirror, เบราว์เซอร์และเครื่องมือจาวาสคริปต์ต่าง ๆ มารวมกันทำให้การดีบักการแจงนับในรูปแบบนี้ง่ายมากและง่ายมาก

การสาธิตการเน้น CodeMirror

const ENUM_COLORENUM_RED   = 0,
      ENUM_COLORENUM_GREEN = 1,
      ENUM_COLORENUM_BLUE  = 2,
      ENUMLEN_COLORENUM    = 3;
var currentColor = ENUM_COLORENUM_GREEN;

if(currentColor === ENUM_COLORENUM_RED) {
   // whatever
}

if(currentColor === ENUM_COLORENUM_DNE) {
   // whatever
}

ในตัวอย่างด้านบนคุณได้รับการแจ้งเตือนด้วยข้อผิดพลาดเนื่องจากENUM_COLORENUM_DNEไม่มีอยู่


เว็

𝗖𝗼𝗻𝗰𝗹𝘂𝘀𝗶𝗼𝗻☑

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

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


เอ๊ะ ฉันชอบอ่านและใช้งานง่ายและเข้าใจในขนาดของโค้ดมาก
แอนดรู

1
@Andrew ด้วยคำตอบของฉันคุณสามารถมีทั้ง ผลลัพธ์คำตอบของฉันเป็นวิธีที่ง่ายที่สุดในการใช้งาน / จัดการโค้ดและขนาดโค้ดที่เล็กที่สุด
แจ็คกิฟฟิน

1
@ แอนดรูว์ฉันพยายามใช้Yet Another Enum (YEA!) ของคุณกับตัวอย่างในโปรแกรมแยกวิเคราะห์สีในคำตอบของฉัน อย่างไรก็ตามฉันพบปัญหาหลายอย่างที่คุณอาจต้องการแก้ไข YEAไม่มีวิธีที่จะขยายการแจกแจงด้วยคลาสย่อยบังคับให้ฉันสร้างคลาสแม่และลูกแยกต่างหากซึ่งอาจยากต่อการจัดการในโครงการขนาดใหญ่ YEAไม่แน่ใจว่ามีรายการ ( colors.REEDผลตอบแทนอดีตundefined) ดังนั้นความผิดพลาดในการพิมพ์จึงทำให้เกิดข้อผิดพลาดที่เข้าใจยาก YEAไม่ได้แยกความแตกต่างระหว่างการใช้การแจกแจงเป็นดัชนีและรหัสซึ่งนำไปสู่การสับสนของรหัสที่ทุกอย่างมีลักษณะเหมือนกัน …
Jack Giffin

1
@Andrew … YEA เป็นอุปสรรคต่อการปิดความสามารถของคอมไพเลอร์ในการลดขนาด เปรียบเทียบซอร์สโค้ดกับ YEA (3549 ไบต์) กับโค้ดย่อด้วย YEA (1344 ไบต์) กับโค้ดย่อด้วยโซลูชันของฉัน (604 ไบต์) ในที่สุด YEA เกี่ยวข้องกับ "การทำแผนที่ตามชื่อ" เพราะมันจะแยกชื่อสตริงจาก ID ที่ระบุ Mine พิจารณาเฉพาะ ID ดังนั้นไม่จำเป็นต้องมี "การจับคู่ตามชื่อ" นำไปสู่การออกแบบที่เรียบง่ายและประสิทธิภาพที่ดีขึ้น ขอบคุณสำหรับการแบ่งปันโซลูชันของคุณ แต่มันต้องการการแก้ไขมากมายก่อนที่จะสามารถนำไปใช้ได้จริง
Jack Giffin

1
@Andrew คุณมีสิทธิ์ที่จะแสดงความคิดเห็นของคุณในขณะที่ฉันกำลังจะขุด👍
Jack Giffin

23

ฉันได้เล่นกับสิ่งนี้เพราะฉันรัก enums ของฉัน =)

การใช้Object.definePropertyฉันคิดว่าฉันคิดวิธีแก้ปัญหาที่ใช้ได้จริง

นี่คือ jsfiddle: http://jsfiddle.net/ZV4A6/

การใช้วิธีนี้ .. คุณควร (ในทางทฤษฎี) สามารถเรียกและกำหนดค่า enum สำหรับวัตถุใด ๆ ได้โดยไม่ส่งผลกระทบต่อคุณลักษณะอื่น ๆ ของวัตถุนั้น

Object.defineProperty(Object.prototype,'Enum', {
    value: function() {
        for(i in arguments) {
            Object.defineProperty(this,arguments[i], {
                value:parseInt(i),
                writable:false,
                enumerable:true,
                configurable:true
            });
        }
        return this;
    },
    writable:false,
    enumerable:false,
    configurable:false
}); 

เนื่องจากคุณลักษณะwritable:falseนี้ควรทำให้พิมพ์ปลอดภัย

ดังนั้นคุณควรจะสามารถสร้างวัตถุที่กำหนดเองแล้วเรียกEnum()มัน ค่าที่กำหนดเริ่มต้นที่ 0 และเพิ่มขึ้นต่อรายการ

var EnumColors={};
EnumColors.Enum('RED','BLUE','GREEN','YELLOW');
EnumColors.RED;    // == 0
EnumColors.BLUE;   // == 1
EnumColors.GREEN;  // == 2
EnumColors.YELLOW; // == 3

3
หากคุณเพิ่มreturn this;ในตอนท้ายของ Enum คุณสามารถทำได้:var EnumColors = {}.Enum('RED','BLUE','GREEN','YELLOW');
HBP

ฉันไม่ได้คิดอย่างนั้นเพราะมันไม่ใช่วิธีปกติในการทำสิ่งต่าง ๆ แต่คุณถูกต้องอย่างแน่นอน! ฉันจะแก้ไขสิ่งต่อไปนี้
ดันแคน

ฉันชอบสิ่งนี้มากแม้ว่าฉันจะไม่ได้เป็นแฟนตัวยงของการเลียนแบบพื้นที่วัตถุ (ด้วยฟังก์ชันระดับโลก ENUM) แปลงสิ่งนี้เป็นฟังก์ชั่น mkenum และเพิ่มการกำหนดตัวเลขที่เป็นตัวเลือก => var mixedUp = mkenum ('BLACK', {RED: 0x0F00, น้ำเงิน: 0X0F, เขียว: 0x0F0, สีขาว: 0x0FFF, หนึ่ง: 1}, สอง: สาม ; // การเพิ่มรหัสของฉันเป็นคำตอบด้านล่าง ขอบคุณ
Andrew Philips

พูดตามตรงฉันไม่ใช้มันอีกแล้ว ฉันใช้ตัวรวบรวมการปิดของ Google และมันใช้งานไม่ได้ดีเกินไป (หรือทำให้ซับซ้อนขึ้น) ถ้าคุณใช้การตั้งค่าขั้นสูง ดังนั้นฉันเพิ่งกลับไปที่เครื่องหมายวัตถุมาตรฐาน
Duncan

1
falseเป็นค่าเริ่มต้นสำหรับwritable, และenumerable configurableไม่จำเป็นต้องเคี้ยวอาหารเป็นค่าเริ่มต้น
ceving

23

ใช้ Javascript Proxies

TLDR:เพิ่มคลาสนี้ให้กับวิธีการยูทิลิตี้ของคุณและใช้ตลอดทั้งรหัสของคุณมันทำให้เกิดพฤติกรรม Enum จากภาษาการเขียนโปรแกรมแบบดั้งเดิมและจริง ๆ แล้วเกิดข้อผิดพลาดเมื่อคุณพยายามเข้าถึงตัวแจงนับที่ไม่มีอยู่หรือเพิ่ม / อัปเดตตัวแจงนับ Object.freeze()ไม่จำเป็นต้องพึ่งพา

class Enum {
  constructor(enumObj) {
    const handler = {
      get(target, name) {
        if (typeof target[name] != 'undefined') {
          return target[name];
        }
        throw new Error(`No such enumerator: ${name}`);
      },
      set() {
        throw new Error('Cannot add/update properties on an Enum instance after it is defined')
      }
    };

    return new Proxy(enumObj, handler);
  }
}

จากนั้นสร้าง enums โดยสร้างอินสแตนซ์ของชั้นเรียน:

const roles = new Enum({
  ADMIN: 'Admin',
  USER: 'User',
});

คำอธิบายแบบเต็ม:

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

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

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

อย่าเข้าใจฉันผิดพฤติกรรมของ JavaScript ในการกลับมาundefinedเมื่อเข้าถึงคุณสมบัติที่ไม่ได้กำหนดนั้นเป็นคุณสมบัติที่ทรงพลังของภาษา แต่มันไม่ใช่คุณสมบัติที่คุณต้องการเมื่อคุณพยายามที่จะเยาะเย้ยโครงสร้าง Enum แบบดั้งเดิม

นี่คือสิ่งที่วัตถุมอบฉันทะส่องแสง พร็อกซี่ได้มาตรฐานในภาษาด้วยการแนะนำของ ES6 (ES2015) นี่คือคำอธิบายจาก MDN:

วัตถุ Proxy ใช้เพื่อกำหนดพฤติกรรมที่กำหนดเองสำหรับการดำเนินงานขั้นพื้นฐาน (เช่นการค้นหาคุณสมบัติการกำหนดการแจงนับการเรียกใช้ฟังก์ชัน ฯลฯ )

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

นี่คือตัวอย่างที่วางแผนไว้ซึ่งใช้วัตถุพร็อกซีเพื่อเลียนแบบ Enums ตัวแจงนับในตัวอย่างนี้เป็นวิธี HTTP มาตรฐาน (เช่น "GET", "POST" ฯลฯ ):

// Class for creating enums (13 lines)
// Feel free to add this to your utility library in 
// your codebase and profit! Note: As Proxies are an ES6 
// feature, some browsers/clients may not support it and 
// you may need to transpile using a service like babel

class Enum {
  // The Enum class instantiates a JavaScript Proxy object.
  // Instantiating a `Proxy` object requires two parameters, 
  // a `target` object and a `handler`. We first define the handler,
  // then use the handler to instantiate a Proxy.

  // A proxy handler is simply an object whose properties
  // are functions which define the behavior of the proxy 
  // when an operation is performed on it. 
  
  // For enums, we need to define behavior that lets us check what enumerator
  // is being accessed and what enumerator is being set. This can be done by 
  // defining "get" and "set" traps.
  constructor(enumObj) {
    const handler = {
      get(target, name) {
        if (typeof target[name] != 'undefined') {
          return target[name]
        }
        throw new Error(`No such enumerator: ${name}`)
      },
      set() {
        throw new Error('Cannot add/update properties on an Enum instance after it is defined')
      }
    }


    // Freeze the target object to prevent modifications
    return new Proxy(enumObj, handler)
  }
}


// Now that we have a generic way of creating Enums, lets create our first Enum!
const httpMethods = new Enum({
  DELETE: "DELETE",
  GET: "GET",
  OPTIONS: "OPTIONS",
  PATCH: "PATCH",
  POST: "POST",
  PUT: "PUT"
})

// Sanity checks
console.log(httpMethods.DELETE)
// logs "DELETE"

try {
  httpMethods.delete = "delete"
} catch (e) {
console.log("Error: ", e.message)
}
// throws "Cannot add/update properties on an Enum instance after it is defined"

try {
  console.log(httpMethods.delete)
} catch (e) {
  console.log("Error: ", e.message)
}
// throws "No such enumerator: delete"


ASIDE: เฮคคืออะไร

ฉันจำได้เมื่อฉันเริ่มเห็นคำว่า proxy ทุกที่มันไม่สมเหตุสมผลสำหรับฉันเป็นเวลานาน ถ้านั่นคือคุณตอนนี้ฉันคิดว่าวิธีง่าย ๆ ในการสรุปพร็อกซี่คือคิดว่าพวกเขาเป็นซอฟต์แวร์สถาบันหรือแม้แต่คนที่ทำหน้าที่เป็นคนกลางหรือคนกลางระหว่างเซิร์ฟเวอร์สอง บริษัท หรือคน


วิธีทำบางอย่างเช่น myEnum.valueOf ("someStringValue")? คาดหวัง: ในกรณีที่สตริงอินพุตมีค่าขององค์ประกอบของตัวแจงนับควรส่งคืนไอเท็ม ในกรณีที่ไม่มีรายการใดมีค่าสตริงนั้นให้ยกเว้น
sscarduzio

@sscarduzio คุณสามารถแทนที่valueOfวิธีการเริ่มต้นโดยระบุว่าเป็นวิธีการเช่นในคลาส Enum อย่างไรก็ตามทำไมคุณถึงต้องการเข้าถึงวิธีนี้เมื่อเทียบกับการเข้าถึงผ่านเครื่องหมายจุด?
Govind Rai

Enum ของฉันคือ const logLevelEnum = Enum ใหม่ ({INFO: "info", DEBUG: "debug"}) และฉันแยกวิเคราะห์จากการป้อนสตริง "info" หรือ "debug" โดยพลการ ดังนั้นฉันต้องการบางอย่างเช่น currentLogLevel = logLevelEnum.parseOrThrow (settings.get ("log_level"))
sscarduzio

1
ทำไมคุณทำlogLevelEnum[settings.get("log_level")]ไม่ได้เหรอ? การเพิ่มparseOrThrowจะเป็นการทำซ้ำกับสิ่งที่กับตัวดักพร็อกซีกำลังทำอยู่เพื่อคุณ
Govind Rai

17

นี่เป็นสิ่งเก่าที่ฉันรู้ แต่วิธีการที่ถูกนำมาใช้ผ่านอินเทอร์เฟซ TypeScript คือ:

var MyEnum;
(function (MyEnum) {
    MyEnum[MyEnum["Foo"] = 0] = "Foo";
    MyEnum[MyEnum["FooBar"] = 2] = "FooBar";
    MyEnum[MyEnum["Bar"] = 1] = "Bar";
})(MyEnum|| (MyEnum= {}));

สิ่งนี้ช่วยให้คุณสามารถค้นหาทั้งMyEnum.Barที่คืนค่า 1 และMyEnum[1]คืนค่า "บาร์" โดยไม่คำนึงถึงลำดับการประกาศ


1
พลัส MyEnum ["บาร์"] ทำงานที่ส่งคืน 1 ... <3 TypeScript จน ...
David Karlaš

3
และแน่นอนถ้าคุณใช้ Typescript จริง ๆ แล้วenum MyEnum { Foo, Bar, Foobar }
รัฐสภา

16

ในES7คุณสามารถใช้ ENUM ที่สง่างามโดยอาศัยคุณลักษณะแบบคงที่:

class ColorEnum  {
    static RED = 0 ;
    static GREEN = 1;
    static BLUE = 2;
}

แล้วก็

if (currentColor === ColorEnum.GREEN ) {/*-- coding --*/}

ข้อได้เปรียบ (จากการใช้คลาสแทนวัตถุตัวอักษร) คือการมีคลาสแม่Enumจากนั้น Enums ทั้งหมดของคุณจะขยายคลาสนั้น

 class ColorEnum  extends Enum {/*....*/}

4
คุณช่วยอธิบายได้ไหมว่าทำไมการมีชั้นผู้ปกครองเป็นข้อได้เปรียบ ฉันรู้สึกว่าฉันขาดอะไรบางอย่าง!
Jon G

7
อย่าทำอย่างนั้น new ColorEnum()ทำให้รู้สึกไม่อย่างแน่นอน
Bergi

3
การขยาย enum ฟังดูบ้าจริงๆ
Codii

เมื่อภาษาไม่รองรับภาษาจะทำให้ความรู้สึกรักษาอนุสัญญานี้และใช้งานเช่นนี้! ฉันเห็นด้วย!
xpto

ฉันคิดว่า (?) สิ่งที่ OP กำลังทำอยู่คือ: ประโยชน์ของสแตติกบริสุทธิ์คือมันมีอยู่ทุกหนทุกแห่งในซิงเกิลและคุณไม่จำเป็นต้องยกระดับชั้นเรียน - OP ไม่แนะนำให้คุณทำ! ผมคิดว่าสิ่งที่เขาพูดคือว่าซับคลาสEnumมีมาตรฐานคงที่วิธีการแจงนับบนมันเหมือนgetValues(), getNames(), iterate()ฯลฯ หากเป็นกรณีที่คุณไม่ต้อง reimplement enumพวกเขาสำหรับแต่ละชนิดใหม่ของ
วิศวกร

15

นี่คือทางออกที่ฉันใช้

function Enum() {
    this._enums = [];
    this._lookups = {};
}

Enum.prototype.getEnums = function() {
    return _enums;
}

Enum.prototype.forEach = function(callback){
    var length = this._enums.length;
    for (var i = 0; i < length; ++i){
        callback(this._enums[i]);
    }
}

Enum.prototype.addEnum = function(e) {
    this._enums.push(e);
}

Enum.prototype.getByName = function(name) {
    return this[name];
}

Enum.prototype.getByValue = function(field, value) {
    var lookup = this._lookups[field];
    if(lookup) {
        return lookup[value];
    } else {
        this._lookups[field] = ( lookup = {});
        var k = this._enums.length - 1;
        for(; k >= 0; --k) {
            var m = this._enums[k];
            var j = m[field];
            lookup[j] = m;
            if(j == value) {
                return m;
            }
        }
    }
    return null;
}

function defineEnum(definition) {
    var k;
    var e = new Enum();
    for(k in definition) {
        var j = definition[k];
        e[k] = j;
        e.addEnum(j)
    }
    return e;
}

และคุณกำหนด enums ของคุณเช่นนี้:

var COLORS = defineEnum({
    RED : {
        value : 1,
        string : 'red'
    },
    GREEN : {
        value : 2,
        string : 'green'
    },
    BLUE : {
        value : 3,
        string : 'blue'
    }
});

และนี่คือวิธีที่คุณเข้าถึง enums:

COLORS.BLUE.string
COLORS.BLUE.value
COLORS.getByName('BLUE').string
COLORS.getByValue('value', 1).string

COLORS.forEach(function(e){
    // do what you want with e
});

ฉันมักจะใช้ 2 วิธีสุดท้ายสำหรับการทำแผนที่ enums จากวัตถุข้อความ

ข้อดีบางประการสำหรับวิธีนี้:

  • ประกาศ enums ง่าย ๆ
  • เข้าถึง enums ของคุณได้ง่าย
  • enums ของคุณอาจเป็นประเภทที่ซับซ้อน
  • คลาส Enum มีการแคชการเชื่อมโยงบางส่วนหากคุณใช้ getByValue บ่อยครั้ง

ข้อเสียบางอย่าง:

  • มีการจัดการหน่วยความจำที่ยุ่งเหยิงเกิดขึ้นในนั้นเพราะฉันเก็บการอ้างอิงไปยัง enums
  • ยังไม่มีความปลอดภัยประเภท

14

สร้างวัตถุตามตัวอักษร:

const Modes = {
  DRAGGING: 'drag',
  SCALING:  'scale',
  CLICKED:  'click'
};

12
constไม่ได้ทำให้คุณสมบัติของวัตถุไม่เปลี่ยนรูป แต่เพียงหมายความว่าตัวแปรModesไม่สามารถกำหนดให้กับสิ่งอื่นได้ เพื่อให้สมบูรณ์มากขึ้นใช้ควบคู่ไปกับObject.freeze() const
rvighne

Object.freezeกรุณาอย่าใช้ มันป้องกันการปิดคอมไพเลอร์จากการฝังวัตถุ
Jack Giffin

11

หากคุณกำลังใช้หัวใจคุณจะได้รับการทำงาน enum เต็มเป่า (หาได้โดย ID, ชื่อสมาชิกที่กำหนดเอง) ฟรีโดยใช้Backbone.Collection

// enum instance members, optional
var Color = Backbone.Model.extend({
    print : function() {
        console.log("I am " + this.get("name"))
    }
});

// enum creation
var Colors = new Backbone.Collection([
    { id : 1, name : "Red", rgb : 0xFF0000},
    { id : 2, name : "Green" , rgb : 0x00FF00},
    { id : 3, name : "Blue" , rgb : 0x0000FF}
], {
    model : Color
});

// Expose members through public fields.
Colors.each(function(color) {
    Colors[color.get("name")] = color;
});

// using
Colors.Red.print()

8

คำตอบของคุณซับซ้อนเกินไป

var buildSet = function(array) {
  var set = {};
  for (var i in array) {
    var item = array[i];
    set[item] = item;
  }
  return set;
}

var myEnum = buildSet(['RED','GREEN','BLUE']);
// myEnum.RED == 'RED' ...etc

1
@ JackGiffin ฉันยอมรับว่าคำตอบของคุณนั้นมีประสิทธิภาพมากกว่าและฉันอาจใช้หน่วยความจำมากกว่านี้ถึงแม้ว่าคุณไม่ควรถือว่าทุกคนต้องการ enum ตามวิธีการที่ C ++ นำมาใช้ โปรดเคารพคำตอบอื่น ๆ และนักพัฒนาที่อาจชอบคำตอบนี้มากกว่าของคุณ
Xeltor

7

ฉันได้แก้ไขโซลูชันของ Andre 'Fi':

  function Enum() {
    var that = this;
    for (var i in arguments) {
        that[arguments[i]] = i;
    }
    this.name = function(value) {
        for (var key in that) {
            if (that[key] == value) {
                return key;
            }
        }
    };
    this.exist = function(value) {
        return (typeof that.name(value) !== "undefined");
    };
    if (Object.freeze) {
        Object.freeze(that);
    }
  }

ทดสอบ:

var Color = new Enum('RED', 'GREEN', 'BLUE');
undefined
Color.name(Color.REDs)
undefined
Color.name(Color.RED)
"RED"
Color.exist(Color.REDs)
false
Color.exist(Color.RED)
true

6

ฉันมากับวิธีการนี้ซึ่งเป็นแบบอย่างหลังจาก enums ใน Java สิ่งเหล่านี้เป็นประเภทที่ปลอดภัยและคุณสามารถinstanceofตรวจสอบได้เช่นกัน

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

var Days = Enum.define("Days", ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]);

Daysตอนนี้หมายถึงDaysenum:

Days.Monday instanceof Days; // true

Days.Friday.name(); // "Friday"
Days.Friday.ordinal(); // 4

Days.Sunday === Days.Sunday; // true
Days.Sunday === Days.Friday; // false

Days.Sunday.toString(); // "Sunday"

Days.toString() // "Days { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday } "

Days.values().map(function(e) { return e.name(); }); //["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
Days.values()[4].name(); //"Friday"

Days.fromName("Thursday") === Days.Thursday // true
Days.fromName("Wednesday").name() // "Wednesday"
Days.Friday.fromName("Saturday").name() // "Saturday"

การดำเนินการ:

var Enum = (function () {
    /**
     * Function to define an enum
     * @param typeName - The name of the enum.
     * @param constants - The constants on the enum. Can be an array of strings, or an object where each key is an enum
     * constant, and the values are objects that describe attributes that can be attached to the associated constant.
     */
    function define(typeName, constants) {

        /** Check Arguments **/
        if (typeof typeName === "undefined") {
            throw new TypeError("A name is required.");
        }

        if (!(constants instanceof Array) && (Object.getPrototypeOf(constants) !== Object.prototype)) {

            throw new TypeError("The constants parameter must either be an array or an object.");

        } else if ((constants instanceof Array) && constants.length === 0) {

            throw new TypeError("Need to provide at least one constant.");

        } else if ((constants instanceof Array) && !constants.reduce(function (isString, element) {
                return isString && (typeof element === "string");
            }, true)) {

            throw new TypeError("One or more elements in the constant array is not a string.");

        } else if (Object.getPrototypeOf(constants) === Object.prototype && !Object.keys(constants).reduce(function (isObject, constant) {
                return Object.getPrototypeOf(constants[constant]) === Object.prototype;
            }, true)) {

            throw new TypeError("One or more constants do not have an associated object-value.");

        }

        var isArray = (constants instanceof Array);
        var isObject = !isArray;

        /** Private sentinel-object used to guard enum constructor so that no one else can create enum instances **/
        function __() { };

        /** Dynamically define a function with the same name as the enum we want to define. **/
        var __enum = new Function(["__"],
            "return function " + typeName + "(sentinel, name, ordinal) {" +
                "if(!(sentinel instanceof __)) {" +
                    "throw new TypeError(\"Cannot instantiate an instance of " + typeName + ".\");" +
                "}" +

                "this.__name = name;" +
                "this.__ordinal = ordinal;" +
            "}"
        )(__);

        /** Private objects used to maintain enum instances for values(), and to look up enum instances for fromName() **/
        var __values = [];
        var __dict = {};

        /** Attach values() and fromName() methods to the class itself (kind of like static methods). **/
        Object.defineProperty(__enum, "values", {
            value: function () {
                return __values;
            }
        });

        Object.defineProperty(__enum, "fromName", {
            value: function (name) {
                var __constant = __dict[name]
                if (__constant) {
                    return __constant;
                } else {
                    throw new TypeError(typeName + " does not have a constant with name " + name + ".");
                }
            }
        });

        /**
         * The following methods are available to all instances of the enum. values() and fromName() need to be
         * available to each constant, and so we will attach them on the prototype. But really, they're just
         * aliases to their counterparts on the prototype.
         */
        Object.defineProperty(__enum.prototype, "values", {
            value: __enum.values
        });

        Object.defineProperty(__enum.prototype, "fromName", {
            value: __enum.fromName
        });

        Object.defineProperty(__enum.prototype, "name", {
            value: function () {
                return this.__name;
            }
        });

        Object.defineProperty(__enum.prototype, "ordinal", {
            value: function () {
                return this.__ordinal;
            }
        });

        Object.defineProperty(__enum.prototype, "valueOf", {
            value: function () {
                return this.__name;
            }
        });

        Object.defineProperty(__enum.prototype, "toString", {
            value: function () {
                return this.__name;
            }
        });

        /**
         * If constants was an array, we can the element values directly. Otherwise, we will have to use the keys
         * from the constants object.
         */
        var _constants = constants;
        if (isObject) {
            _constants = Object.keys(constants);
        }

        /** Iterate over all constants, create an instance of our enum for each one, and attach it to the enum type **/
        _constants.forEach(function (name, ordinal) {
            // Create an instance of the enum
            var __constant = new __enum(new __(), name, ordinal);

            // If constants was an object, we want to attach the provided attributes to the instance.
            if (isObject) {
                Object.keys(constants[name]).forEach(function (attr) {
                    Object.defineProperty(__constant, attr, {
                        value: constants[name][attr]
                    });
                });
            }

            // Freeze the instance so that it cannot be modified.
            Object.freeze(__constant);

            // Attach the instance using the provided name to the enum type itself.
            Object.defineProperty(__enum, name, {
                value: __constant
            });

            // Update our private objects
            __values.push(__constant);
            __dict[name] = __constant;
        });

        /** Define a friendly toString method for the enum **/
        var string = typeName + " { " + __enum.values().map(function (c) {
                return c.name();
            }).join(", ") + " } ";

        Object.defineProperty(__enum, "toString", {
            value: function () {
                return string;
            }
        });

        /** Freeze our private objects **/
        Object.freeze(__values);
        Object.freeze(__dict);

        /** Freeze the prototype on the enum and the enum itself **/
        Object.freeze(__enum.prototype);
        Object.freeze(__enum);

        /** Return the enum **/
        return __enum;
    }

    return {
        define: define
    }

})();

มันดูดีบางทีคุณควรตรวจสอบว่ามีfreezeเมธอดสำหรับความเข้ากันได้แบบย้อนหลังหรือไม่? เช่น,if (Object.freeze) { Object.freeze(values); }
FBB

จุดดี! จะทำ!
Vivin Paliath

6

IE8 ไม่รองรับเมธอด freeze ()
ที่มา: http://kangax.github.io/compat-table/es5/คลิกที่ "แสดงเบราว์เซอร์ที่ล้าสมัย?" ด้านบนและตรวจสอบ IE8 & ตรึงการแยกแถว col

ในโครงการเกมปัจจุบันของฉันฉันใช้ด้านล่างเนื่องจากลูกค้าบางรายยังใช้ IE8:

var CONST_WILD_TYPES = {
    REGULAR: 'REGULAR',
    EXPANDING: 'EXPANDING',
    STICKY: 'STICKY',
    SHIFTING: 'SHIFTING'
};

เราสามารถทำได้เช่นกัน:

var CONST_WILD_TYPES = {
    REGULAR: 'RE',
    EXPANDING: 'EX',
    STICKY: 'ST',
    SHIFTING: 'SH'
};

หรือแม้กระทั่งสิ่งนี้:

var CONST_WILD_TYPES = {
    REGULAR: '1',
    EXPANDING: '2',
    STICKY: '3',
    SHIFTING: '4'
};

อันสุดท้ายดูเหมือนจะมีประสิทธิภาพมากที่สุดสำหรับสตริงมันลดแบนด์วิดท์ทั้งหมดของคุณหากคุณมีเซิร์ฟเวอร์และไคลเอนต์แลกเปลี่ยนข้อมูลนี้
แน่นอนว่าตอนนี้เป็นหน้าที่ของคุณที่จะต้องแน่ใจว่าไม่มีความขัดแย้งในข้อมูล (RE, EX, ฯลฯ จะต้องไม่ซ้ำกันเช่นกันเช่น 1, 2 และอื่น ๆ จะต้องไม่ซ้ำกัน) โปรดทราบว่าคุณต้องบำรุงรักษาสิ่งเหล่านี้ตลอดไปเพื่อความเข้ากันได้แบบย้อนหลัง

ที่ได้รับมอบหมาย:

var wildType = CONST_WILD_TYPES.REGULAR;

เปรียบเทียบ:

if (wildType === CONST_WILD_TYPES.REGULAR) {
    // do something here
}

5
var ColorEnum = {
    red: {},
    green: {},
    blue: {}
}

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


คำตอบนี้ถูกประเมินค่าน้อยเกินไป มันเป็นหนึ่งในแนวคิดที่ฉันโปรดปรานสำหรับความเรียบง่าย ในทางปฏิบัติฉันคิดว่าฉันจะติดกับสตริงเพราะตอนนี้การดีบักง่ายขึ้น
Domino

อืมตรวจสอบให้แน่ใจว่าโค้ดนี้จะไม่ถูกเรียกสองครั้ง ...
แอนดรูว์

4

นี่คือสองสามวิธีที่ต่างกันในการดำเนินการenums typescript

วิธีที่ง่ายที่สุดคือการวนซ้ำวัตถุการเพิ่มคู่คีย์ - ค่ากลับลงในวัตถุ ข้อเสียเปรียบเพียงอย่างเดียวคือคุณต้องตั้งค่าสำหรับสมาชิกแต่ละคนด้วยตนเอง

function _enum(list) {       
  for (var key in list) {
    list[list[key] = list[key]] = key;
  }
  return Object.freeze(list);
}

var Color = _enum({
  Red: 0,
  Green: 5,
  Blue: 2
});

// Color → {0: "Red", 2: "Blue", 5: "Green", "Red": 0, "Green": 5, "Blue": 2}
// Color.Red → 0
// Color.Green → 5
// Color.Blue → 2
// Color[5] → Green
// Color.Blue > Color.Green → false


และนี่คือlodash mixinเพื่อสร้าง enum โดยใช้สตริง ในขณะที่รุ่นนี้มีส่วนเกี่ยวข้องเพิ่มขึ้นเล็กน้อย แต่จะมีการกำหนดหมายเลขโดยอัตโนมัติสำหรับคุณ วิธีการ Lodash ทั้งหมดที่ใช้ในตัวอย่างนี้มีค่าเทียบเท่า JavaScript ปกติดังนั้นคุณสามารถเปลี่ยนได้อย่างง่ายดายหากคุณต้องการ

function enum() {
    var key, val = -1, list = {};
    _.reduce(_.toArray(arguments), function(result, kvp) {    
        kvp = kvp.split("=");
        key = _.trim(kvp[0]);
        val = _.parseInt(kvp[1]) || ++val;            
        result[result[val] = key] = val;
        return result;
    }, list);
    return Object.freeze(list);
}    

// Add enum to lodash 
_.mixin({ "enum": enum });

var Color = _.enum(
    "Red",
    "Green",
    "Blue = 5",
    "Yellow",
    "Purple = 20",
    "Gray"
);

// Color.Red → 0
// Color.Green → 1
// Color.Blue → 5
// Color.Yellow → 6
// Color.Purple → 20
// Color.Gray → 21
// Color[5] → Blue

ฉลาดมากขอบคุณ
Ilan

4

ฉันเพิ่งเผยแพร่แพคเกจ NPM gen_enumช่วยให้คุณสร้างโครงสร้างข้อมูล Enum ใน Javascript ได้อย่างรวดเร็ว:

var genEnum = require('gen_enum');

var AppMode = genEnum('SIGN_UP, LOG_IN, FORGOT_PASSWORD');
var curMode = AppMode.LOG_IN;
console.log(curMode.isLogIn()); // output true 
console.log(curMode.isSignUp()); // output false 
console.log(curMode.isForgotPassword()); // output false 

สิ่งหนึ่งที่ดีเกี่ยวกับเครื่องมือเล็ก ๆ นี้คือในสภาพแวดล้อมที่ทันสมัย ​​(รวมถึง nodejs และเบราว์เซอร์ IE 9+) วัตถุ Enum ที่คืนมานั้นไม่เปลี่ยนรูป

สำหรับข้อมูลเพิ่มเติมกรุณาชำระเงินhttps://github.com/greenlaw110/enumjs

อัพเดท

ฉันเลิกใช้gen_enumแพ็คเกจและรวมฟังก์ชั่นเป็นแพคเกจconstjsซึ่งมีคุณสมบัติเพิ่มเติมรวมถึงวัตถุที่ไม่เปลี่ยนรูป, การดีซีเรียลสตริง JSON, ค่าคงที่สตริงและการสร้างบิตแมปเป็นต้นชำระเงินhttps://www.npmjs.com/package/constjsสำหรับข้อมูลเพิ่มเติม

หากต้องการอัปเกรดจากgen_enumเป็นconstjsเพียงเปลี่ยนคำสั่ง

var genEnum = require('gen_enum');

ถึง

var genEnum = require('constjs').enum;

4

วิธีที่ง่ายที่สุด:

สร้าง

var Status = Object.freeze({
    "Connecting":0,
    "Ready":1,
    "Loading":2,
    "Processing": 3
});

รับความคุ้มค่า

console.log(Status.Ready) // 1

รับรหัส

console.log(Object.keys(Status)[Status.Ready]) // Ready

4

ฉันได้สร้างคลาส Enum ที่สามารถดึงค่าและชื่อที่ O (1) นอกจากนี้ยังสามารถสร้าง Object Array ที่มีชื่อและค่าทั้งหมด

function Enum(obj) {
    // Names must be unique, Values do not.
    // Putting same values for different Names is risky for this implementation

    this._reserved = {
        _namesObj: {},
        _objArr: [],
        _namesArr: [],
        _valuesArr: [],
        _selectOptionsHTML: ""
    };

    for (k in obj) {
        if (obj.hasOwnProperty(k)) {
            this[k] = obj[k];
            this._reserved._namesObj[obj[k]] = k;
        }
    }
}
(function () {
    this.GetName = function (val) {
        if (typeof this._reserved._namesObj[val] === "undefined")
            return null;
        return this._reserved._namesObj[val];
    };

    this.GetValue = function (name) {
        if (typeof this[name] === "undefined")
            return null;
        return this[name];
    };

    this.GetObjArr = function () {
        if (this._reserved._objArr.length == 0) {
            var arr = [];
            for (k in this) {
                if (this.hasOwnProperty(k))
                    if (k != "_reserved")
                        arr.push({
                            Name: k,
                            Value: this[k]
                        });
            }
            this._reserved._objArr = arr;
        }
        return this._reserved._objArr;
    };

    this.GetNamesArr = function () {
        if (this._reserved._namesArr.length == 0) {
            var arr = [];
            for (k in this) {
                if (this.hasOwnProperty(k))
                    if (k != "_reserved")
                        arr.push(k);
            }
            this._reserved._namesArr = arr;
        }
        return this._reserved._namesArr;
    };

    this.GetValuesArr = function () {
        if (this._reserved._valuesArr.length == 0) {
            var arr = [];
            for (k in this) {
                if (this.hasOwnProperty(k))
                    if (k != "_reserved")
                        arr.push(this[k]);
            }
            this._reserved._valuesArr = arr;
        }
        return this._reserved._valuesArr;
    };

    this.GetSelectOptionsHTML = function () {
        if (this._reserved._selectOptionsHTML.length == 0) {
            var html = "";
            for (k in this) {
                if (this.hasOwnProperty(k))
                    if (k != "_reserved")
                        html += "<option value='" + this[k] + "'>" + k + "</option>";
            }
            this._reserved._selectOptionsHTML = html;
        }
        return this._reserved._selectOptionsHTML;
    };
}).call(Enum.prototype);

คุณสามารถเริ่มต้นเช่นนี้:

var enum1 = new Enum({
    item1: 0,
    item2: 1,
    item3: 2
});

วิธีดึงค่า (เช่น Enums ใน C #):

var val2 = enum1.item2;

ในการดึงชื่อสำหรับค่า (สามารถคลุมเครือเมื่อใส่ค่าเดียวกันสำหรับชื่ออื่น):

var name1 = enum1.GetName(0);  // "item1"

ในการรับอาร์เรย์ที่มีชื่อและค่าแต่ละรายการในวัตถุ:

var arr = enum1.GetObjArr();

จะสร้าง:

[{ Name: "item1", Value: 0}, { ... }, ... ]

คุณยังสามารถรับตัวเลือก html ได้อย่างง่ายดาย:

var html = enum1.GetSelectOptionsHTML();

ซึ่งถือ:

"<option value='0'>item1</option>..."

4

แม้ว่าจะรองรับเฉพาะวิธีการแบบคงที่ (และไม่ใช่คุณสมบัติแบบคงที่) ได้รับการสนับสนุนใน ES2015 (ดูที่นี่เช่นกัน§15.2.2.2) อยากรู้อยากเห็นคุณสามารถใช้ด้านล่างกับ Babel กับที่es2015ตั้งไว้ล่วงหน้า:

class CellState {
    v: string;
    constructor(v: string) {
        this.v = v;
        Object.freeze(this);
    }
    static EMPTY       = new CellState('e');
    static OCCUPIED    = new CellState('o');
    static HIGHLIGHTED = new CellState('h');
    static values      = function(): Array<CellState> {
        const rv = [];
        rv.push(CellState.EMPTY);
        rv.push(CellState.OCCUPIED);
        rv.push(CellState.HIGHLIGHTED);
        return rv;
    }
}
Object.freeze(CellState);

ฉันพบสิ่งนี้จะทำงานได้ตามที่คาดไว้แม้ในโมดูล (เช่นการนำเข้าCellStateenum จากโมดูลอื่น) และเมื่อฉันนำเข้าโมดูลโดยใช้ Webpack

ข้อได้เปรียบวิธีนี้มีมากกว่าคำตอบอื่น ๆ ส่วนใหญ่คือคุณสามารถใช้ร่วมกับตัวตรวจสอบชนิดคงที่ (เช่นการไหล ) และคุณสามารถยืนยันในเวลาการพัฒนาโดยใช้การตรวจสอบชนิดคงที่ว่าตัวแปรพารามิเตอร์ ฯลฯ มีเฉพาะCellState" enum "แทนที่จะเป็น enum อื่น ๆ (ซึ่งเป็นไปไม่ได้ที่จะแยกแยะถ้าคุณใช้วัตถุหรือสัญลักษณ์ทั่วไป)

ปรับปรุง

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

  1. ห้ามสร้างวัตถุประเภทอีกต่อCellStateไป
  2. คุณรับประกันได้ว่าจะไม่มีการกำหนด Enum สองอินสแตนซ์ให้กับรหัสเดียวกัน
  3. วิธีการยูทิลิตี้ที่จะได้รับกลับ enum จากการเป็นตัวแทนสตริง
  4. valuesฟังก์ชั่นที่ให้ผลตอบแทนทุกกรณี enum ไม่ได้มีการสร้างค่าตอบแทนในข้างต้นคู่มือ (และผิดพลาดได้ง่าย) วิธีการ

    'use strict';
    
    class Status {
    
    constructor(code, displayName = code) {
        if (Status.INSTANCES.has(code))
            throw new Error(`duplicate code value: [${code}]`);
        if (!Status.canCreateMoreInstances)
            throw new Error(`attempt to call constructor(${code}`+
           `, ${displayName}) after all static instances have been created`);
        this.code        = code;
        this.displayName = displayName;
        Object.freeze(this);
        Status.INSTANCES.set(this.code, this);
    }
    
    toString() {
        return `[code: ${this.code}, displayName: ${this.displayName}]`;
    }
    static INSTANCES   = new Map();
    static canCreateMoreInstances      = true;
    
    // the values:
    static ARCHIVED    = new Status('Archived');
    static OBSERVED    = new Status('Observed');
    static SCHEDULED   = new Status('Scheduled');
    static UNOBSERVED  = new Status('Unobserved');
    static UNTRIGGERED = new Status('Untriggered');
    
    static values      = function() {
        return Array.from(Status.INSTANCES.values());
    }
    
    static fromCode(code) {
        if (!Status.INSTANCES.has(code))
            throw new Error(`unknown code: ${code}`);
        else
            return Status.INSTANCES.get(code);
    }
    }
    
    Status.canCreateMoreInstances = false;
    Object.freeze(Status);
    exports.Status = Status;

ตัวอย่างที่ดี :-)
Ashraf.Shk786

4

วิธี es7, (ตัววนซ้ำแช่แข็ง), การใช้งาน:

const ThreeWiseMen = new Enum('Melchior', 'Caspar', 'Balthazar')

for (let name of ThreeWiseMen)
    console.log(name)


// with a given key
let key = ThreeWiseMen.Melchior

console.log(key in ThreeWiseMen) // true (string conversion, also true: 'Melchior' in ThreeWiseMen)

for (let entry from key.enum)
     console.log(entry)


// prevent alteration (throws TypeError in strict mode)
ThreeWiseMen.Me = 'Me too!'
ThreeWiseMen.Melchior.name = 'Foo'

รหัส:

class EnumKey {

    constructor(props) { Object.freeze(Object.assign(this, props)) }

    toString() { return this.name }

}

export class Enum {

    constructor(...keys) {

        for (let [index, key] of keys.entries()) {

            Object.defineProperty(this, key, {

                value: new EnumKey({ name:key, index, enum:this }),
                enumerable: true,

            })

        }

        Object.freeze(this)

    }

    *[Symbol.iterator]() {

        for (let key of Object.keys(this))
            yield this[key]

    }

    toString() { return [...this].join(', ') }

}

4

นี่คือวิธีที่ typescript แปลมันenumลงใน Javascript:

var makeEnum = function(obj) {
    obj[ obj['Active'] = 1 ] = 'Active';
    obj[ obj['Closed'] = 2 ] = 'Closed';
    obj[ obj['Deleted'] = 3 ] = 'Deleted';
}

ขณะนี้:

makeEnum( NewObj = {} )
// => {1: "Active", 2: "Closed", 3: "Deleted", Active: 1, Closed: 2, Deleted: 3}

ตอนแรกฉันสับสนว่าทำไมobj[1]ผลตอบแทน'Active'แต่แล้วก็รู้ว่ามันตายง่าย - ผู้ประกอบการมอบหมายกำหนดค่าแล้วส่งคืน:

obj['foo'] = 1
// => 1

4

คุณสามารถทำอะไรเช่นนี้

    var Enum = (function(foo) {

    var EnumItem = function(item){
        if(typeof item == "string"){
            this.name = item;
        } else {
            this.name = item.name;
        }
    }
    EnumItem.prototype = new String("DEFAULT");
    EnumItem.prototype.toString = function(){
        return this.name;
    }
    EnumItem.prototype.equals = function(item){
        if(typeof item == "string"){
            return this.name == item;
        } else {
            return this == item && this.name == item.name;
        }
    }

    function Enum() {
        this.add.apply(this, arguments);
        Object.freeze(this);
    }
    Enum.prototype.add = function() {
        for (var i in arguments) {
            var enumItem = new EnumItem(arguments[i]);
            this[enumItem.name] = enumItem;
        }
    };
    Enum.prototype.toList = function() {
        return Object.keys(this);
    };
    foo.Enum = Enum;
    return Enum;
})(this);
var STATUS = new Enum("CLOSED","PENDING", { name : "CONFIRMED", ackd : true });
var STATE = new Enum("CLOSED","PENDING","CONFIRMED",{ name : "STARTED"},{ name : "PROCESSING"});

ตามที่กำหนดไว้ในห้องสมุดนี้ https://github.com/webmodule/foo/blob/master/foo.js#L217

ตัวอย่างที่สมบูรณ์ https://gist.github.com/lnt/bb13a2fd63cdb8bce85fd62965a20026


3

วิธีที่ง่ายและรวดเร็วคือ:

var Colors = function(){
return {
    'WHITE':0,
    'BLACK':1,
    'RED':2,
    'GREEN':3
    }
}();

console.log(Colors.WHITE)  //this prints out "0"

6
ฟังก์ชั่นนี้ไม่จำเป็นและให้ผลลัพธ์ที่แน่นอนเหมือนกับที่ OP โพสต์ไว้
Sildoreth

3

ในฐานะที่เป็นลายลักษณ์อักษรตุลาคม 2014 - ดังนั้นนี่คือทางออกร่วมสมัย กำลังเขียนวิธีแก้ปัญหาเป็น Node Module และได้รวมการทดสอบโดยใช้ Mocha และ Chai รวมถึงขีดล่าง JS คุณสามารถเพิกเฉยสิ่งเหล่านี้ได้อย่างง่ายดายและใช้รหัส Enum หากต้องการ

เห็นโพสต์จำนวนมากที่มีห้องสมุดที่ซับซ้อนเกินไปและอื่น ๆ วิธีการรับการสนับสนุน Enum ใน Javascript นั้นง่ายมากไม่จำเป็น นี่คือรหัส:

ไฟล์: enums.js

_ = require('underscore');

var _Enum = function () {

   var keys = _.map(arguments, function (value) {
      return value;
   });
   var self = {
      keys: keys
   };
   for (var i = 0; i < arguments.length; i++) {
      self[keys[i]] = i;
   }
   return self;
};

var fileFormatEnum = Object.freeze(_Enum('CSV', 'TSV'));
var encodingEnum = Object.freeze(_Enum('UTF8', 'SHIFT_JIS'));

exports.fileFormatEnum = fileFormatEnum;
exports.encodingEnum = encodingEnum;

และการทดสอบเพื่อแสดงสิ่งที่ให้คุณ:

ไฟล์: enumsSpec.js

var chai = require("chai"),
    assert = chai.assert,
    expect = chai.expect,
    should = chai.should(),
    enums = require('./enums'),
    _ = require('underscore');


describe('enums', function () {

    describe('fileFormatEnum', function () {
        it('should return expected fileFormat enum declarations', function () {
            var fileFormatEnum = enums.fileFormatEnum;
            should.exist(fileFormatEnum);
            assert('{"keys":["CSV","TSV"],"CSV":0,"TSV":1}' === JSON.stringify(fileFormatEnum), 'Unexpected format');
            assert('["CSV","TSV"]' === JSON.stringify(fileFormatEnum.keys), 'Unexpected keys format');
        });
    });

    describe('encodingEnum', function () {
        it('should return expected encoding enum declarations', function () {
            var encodingEnum = enums.encodingEnum;
            should.exist(encodingEnum);
            assert('{"keys":["UTF8","SHIFT_JIS"],"UTF8":0,"SHIFT_JIS":1}' === JSON.stringify(encodingEnum), 'Unexpected format');
            assert('["UTF8","SHIFT_JIS"]' === JSON.stringify(encodingEnum.keys), 'Unexpected keys format');
        });
    });

});

อย่างที่คุณเห็นคุณจะได้โรงงาน Enum คุณสามารถรับกุญแจทั้งหมดได้ง่ายๆโดยการเรียก enum.keys และคุณสามารถจับคู่คีย์กับค่าคงที่จำนวนเต็มได้ และคุณสามารถนำโรงงานกลับมาใช้ใหม่ด้วยค่าที่แตกต่างกันและส่งออก Enums ที่สร้างขึ้นโดยใช้วิธีการแยกส่วนของโหนด

อีกครั้งหากคุณเป็นผู้ใช้ที่ไม่เป็นทางการหรือในเบราว์เซอร์ ฯลฯ ให้นำส่วนหนึ่งของรหัสออกจากโรงงานและอาจลบไลบรารีขีดล่างด้วยหากคุณไม่ต้องการใช้ในรหัสของคุณ


5
คุณสามารถโพสต์คำตอบด้วยคำว่า "นี่คือวิธีการทำเช่นนี้ในฐานะผู้ใช้ทั่วไปที่ต้องการเพียงแค่ enums ไม่ใช่โรงงานขีดเส้นใต้หรือสิ่งอื่นใดที่แฟนซี"?
GreenAsJade

5
แม้ว่ามันจะยอดเยี่ยมจากผู้พัฒนา eyepoint แต่ก็ไม่ได้ทำความสะอาดหรืออ่านได้ วิธีการแก้ปัญหา Enum จาก OP นั้นง่ายขึ้นและอ่านได้ง่ายขึ้นในทุก ๆ ทางและใช้งานได้ดีกว่า ถึงกระนั้นก็ค่อนข้างน่ากลัวที่คุณมากับสิ่งนี้
David

3

ฉันคิดว่ามันใช้งานง่าย https://stackoverflow.com/a/32245370/4365315

var A = {a:11, b:22}, 
enumA = new TypeHelper(A);

if(enumA.Value === A.b || enumA.Key === "a"){ 
... 
}

var keys = enumA.getAsList();//[object, object]

//set
enumA.setType(22, false);//setType(val, isKey)

enumA.setType("a", true);

enumA.setTypeByIndex(1);

UPDATE:

มีรหัสผู้ช่วยของฉัน ( TypeHelper)

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