ฉันจะสร้าง "ฟิลด์คงที่สาธารณะ" ในคลาส ES6 ได้อย่างไร


86

ฉันกำลังสร้างคลาส Javascript และฉันต้องการมีฟิลด์แบบคงที่สาธารณะเช่นใน Java นี่คือรหัสที่เกี่ยวข้อง:

export default class Agent {
    CIRCLE: 1,
    SQUARE: 2,
    ...

นี่คือข้อผิดพลาดที่ฉันได้รับ:

line 2, col 11, Class properties must be methods. Expected '(' but instead saw ':'.

ดูเหมือนว่าโมดูล ES6 จะไม่อนุญาต มีวิธีทำให้ได้พฤติกรรมที่ต้องการหรือไม่หรือต้องเขียน getter?


คุณใช้เอ็นจิ้น ECMAScript 6 รุ่นใด
ได

คำตอบ:


136

คุณสร้าง "public static field" โดยใช้ accessor และคีย์เวิร์ด "static":

class Agent {
    static get CIRCLE() {
      return 1;
    }
    static get SQUARE() {
      return 2;
    }
}

Agent.CIRCLE; // 1

ดูข้อมูลจำเพาะ14.5 - คำจำกัดความของคลาส - คุณจะเห็นบางสิ่งที่เกี่ยวข้องอย่างน่าสงสัย :)

ClassElement [ผลตอบแทน]:
  MethodDefinition [? Yield]
  คง MethodDefinition [? Yield];

จากตรงนั้นคุณสามารถทำตาม14.5.14 - Runtime Semantics: ClassDefinitionEvaluation - เพื่อตรวจสอบอีกครั้งว่ามันทำอย่างที่มันเป็นจริงหรือไม่ โดยเฉพาะขั้นตอนที่ 20:

  1. สำหรับแต่ละ ClassElement m ตามลำดับจากวิธีการ
    1. ถ้าIsStatic ของ m เป็นเท็จดังนั้น
      1. ให้สถานะเป็นผลลัพธ์ของการดำเนินการ PropertyDefinitionEvaluation สำหรับ m พร้อมอาร์กิวเมนต์โปรโตและ false
    2. อื่น,
      1. ให้สถานะเป็นผลลัพธ์ของการดำเนินการ PropertyDefinitionEvaluation สำหรับ m ที่มีอาร์กิวเมนต์ F และ false
    3. หากสถานะเป็นการเสร็จสิ้นทันที
      1. ตั้งค่า LexicalEnvironment ของบริบทการเรียกใช้งานเป็น lex
      2. ส่งคืนสถานะ

IsStatic ถูกกำหนดไว้ก่อนหน้านี้ใน14.5.9

ClassElement: static MethodDefinition
Return true

ดังนั้นจึงPropertyMethodDefinitionถูกเรียกด้วย "F" (ตัวสร้างวัตถุฟังก์ชัน) เป็นอาร์กิวเมนต์ซึ่งในทางกลับกันจะสร้างวิธีการเข้าถึงบนวัตถุนั้น

สิ่งนี้ใช้งานได้อย่างน้อย IETP (ตัวอย่างเทคโนโลยี) เช่นเดียวกับคอมไพเลอร์ 6to5 และ Traceur


สำหรับใครที่กำลังมองหาคุณสมบัติ Static accessor ยังไม่รองรับใน Node : - / kangax.github.io/compat-table/es6/…
David Hernandez

1
อย่างน้อย Node.js 6.x + นี้ได้รับการสนับสนุน
NuSkooler

โปรดทราบว่าหากคุณใช้โฟลว์คุณต้องเพิ่มบรรทัดunsafe.enable_getters_and_setters=trueใน. flowconfig ของคุณภายใต้[options](ซึ่งน่ารำคาญ)
kristina

สิ่งนี้ใช้ไม่ได้สำหรับฉันฉันได้รับ `` Unhandled ปฏิเสธ TypeError: ไม่สามารถตั้งค่าคุณสมบัติ dataHashKey ของคอลเล็กชันคลาส {api_1 | คงรับ dataHashKey () {api_1 | กลับ 'คอลเลกชัน'; api_1 | } ``
Pavan

54

มีข้อเสนอ Stage 3 ECMAScript ที่เรียกว่า"Static Class Features"โดย Daniel Ehrenberg และ Jeff Morrison ที่มีจุดมุ่งหมายเพื่อแก้ปัญหานี้ ควบคู่ไปกับข้อเสนอ"เขตข้อมูลชั้นเรียน"ขั้นที่ 3 รหัสในอนาคตจะมีลักษณะดังนี้:

class MyClass {
    static myStaticProp = 42;
    myProp = 42;
    myProp2 = this.myProp;
    myBoundFunc = () => { console.log(this.myProp); };

    constructor() {
        console.log(MyClass.myStaticProp); // Prints '42'
        console.log(this.myProp); // Prints '42'
        this.myBoundFunc(); // Prints '42'
    }
}

ข้างต้นเทียบเท่ากับ:

class MyClass {
    constructor() {
        this.myProp = 42;
        this.myProp2 = this.myProp;
        this.myBoundFunc = () => { console.log(this.myProp); };

        console.log(MyClass.myStaticProp); // Prints '42'
        console.log(this.myProp); // Prints '42'
        this.myBoundFunc(); // Prints '42'
    }
}
MyClass.myStaticProp = 42;

Babel รองรับช่องคลาส Transpiling ผ่าน@ babel / plugin-offer-class-properties (รวมอยู่ในค่าที่ตั้งไว้ล่วงหน้าของ stage-3 ) เพื่อให้คุณสามารถใช้คุณสมบัตินี้ได้แม้ว่ารันไทม์ JavaScript ของคุณจะไม่รองรับก็ตาม


เมื่อเทียบกับโซลูชันของ @ kangax ในการประกาศ getter แล้วโซลูชันนี้ยังมีประสิทธิภาพมากกว่าเนื่องจากที่นี่มีการเข้าถึงคุณสมบัติโดยตรงแทนที่จะเรียกใช้ฟังก์ชัน

หากข้อเสนอนี้ได้รับการยอมรับก็จะสามารถเขียนโค้ด JavaScript ในลักษณะที่คล้ายกับภาษาเชิงวัตถุแบบดั้งเดิมเช่น Java และC♯มากขึ้น


แก้ไข : ข้อเสนอฟิลด์คลาสแบบรวมอยู่ในขั้นตอนที่ 3 อัปเดตเป็นแพ็คเกจ Babel v7.x

แก้ไข (ก.พ. 2020) : ฟีเจอร์คลาสแบบคงที่ถูกแบ่งออกเป็นข้อเสนออื่น ขอบคุณ @ GOTO0!


ฉันคิดว่าข้อเสนอที่เกี่ยวข้องเป็นข้อเสนอนี้ ( คุณสมบัติคลาสคงที่ )
GOTO 0

29

ในร่างปัจจุบันของ ECMAScript 6 (ณ เดือนกุมภาพันธ์ 2015) คุณสมบัติของคลาสทั้งหมดต้องเป็นวิธีการไม่ใช่ค่า (หมายเหตุใน ECMAScript "คุณสมบัติ" มีความคล้ายคลึงกันในแนวคิดกับฟิลด์ OOP ยกเว้นค่าฟิลด์ต้องเป็นFunctionอ็อบเจ็กต์ไม่ใช่ค่าใด ๆ ค่าอื่น ๆ เช่น a NumberหรือObject)

คุณยังคงสามารถระบุสิ่งเหล่านี้ได้โดยใช้ตัวระบุคุณสมบัติตัวสร้าง ECMAScript แบบดั้งเดิม:

 class Agent {
 }
 Agent.CIRCLE = 1;
 Agent.SQUARE = 2;
 ...

11
โปรดทราบว่าclassไวยากรณ์ES6 เป็นเพียงน้ำตาลทางไวยากรณ์สำหรับฟังก์ชันตัวสร้าง JS แบบดั้งเดิมและต้นแบบเท่านั้น
Matt Browne

ฉันคิดว่าคุณต้องการใส่คุณสมบัติเหล่านั้นบนต้นแบบไม่ใช่บนตัวสร้างเพื่อให้สามารถมองเห็นได้ผ่านการอ้างอิงคุณสมบัติจากอินสแตนซ์
Pointy

@Pointy ฉันสรุปได้ว่า OP พยายามจัดเก็บค่าคงที่เพื่ออ้างอิง (เกือบจะเหมือนกับ C # /. NET enum)
ได

2
@MattBrowne ใช่ แต่classไวยากรณ์ที่ชัดเจนยังมีความแตกต่างที่เหมาะสม ตัวอย่างเช่นวิธีการที่ประกาศด้วยClass.prototype.method = function () {};สามารถระบุได้ (มองเห็นได้ด้วยลูป for-in) ในขณะที่classวิธีการไม่สามารถแจกแจง
Timothy Gu

4

เพื่อให้ได้ประโยชน์สูงสุดจากตัวแปรคงที่ฉันทำตามแนวทางนี้ เพื่อให้เจาะจงมากขึ้นเราสามารถใช้มันเพื่อใช้ตัวแปรส่วนตัวหรือมีเฉพาะ public getter หรือมีทั้ง getter หรือ setter ในกรณีสุดท้ายจะเหมือนกับวิธีแก้ปัญหาที่โพสต์ไว้ด้านบน

var Url = (() => {
    let _staticMember = [];
    return class {
        static getQueries(hash = document.location.hash) {
            return hash;
        }

        static get staticMember(){
            return _staticMember;
        }
    };
})();

Usages:
console.log(Url.staticMember); // [];
Url.staticMember.push('it works');
console.log(Url.staticMember); // ['it works'];

ฉันสามารถสร้างคลาสอื่นที่ขยาย Url และใช้งานได้

ฉันใช้ babel เพื่อแปลงรหัส ES6 เป็น ES5


1
“ ประโยชน์เต็มที่” คืออะไร? คงจะไม่class Url { static getQueries… }; Url.staticMember = [];ง่ายไปกว่านี้แล้วหรือ?
Bergi

===การเปรียบเทียบเหล่านั้นทั้งผลตอบแทนfalsebtw
Bergi

"ข้อได้เปรียบเต็มรูปแบบ" หมายความว่าด้วยวิธีข้างต้นคุณสามารถเก็บ _staticMember ไว้เป็นส่วนตัวได้หากต้องการ
SM Adnan

-1

คำตอบของ @kangax ไม่ได้เลียนแบบพฤติกรรมคงที่ทั้งหมดของภาษา OOP แบบดั้งเดิมเนื่องจากคุณไม่สามารถเข้าถึงคุณสมบัติคงที่ได้จากอินสแตนซ์เช่น const agent = new Agent; agent.CIRCLE; // Undefined

หากคุณต้องการเข้าถึงคุณสมบัติคงที่เหมือนกับ OOP นี่คือทางออกของฉัน:

class NewApp {
  get MULTIPLE_VERSIONS_SUPPORTED() {
    return this.constructor.MULTIPLE_VERSIONS_SUPPORTED; // Late binding for inheritance
  }
}

NewApp.MULTIPLE_VERSIONS_SUPPORTED = true;

รหัสทดสอบดังนี้.

class NewApp {
  get MULTIPLE_VERSIONS_SUPPORTED() {
    console.log('this.constructor.name:', this.constructor.name); // late binding
    return this.constructor.MULTIPLE_VERSIONS_SUPPORTED;
  }
}

// Static property can be accessed by class
NewApp.MULTIPLE_VERSIONS_SUPPORTED = true;

const newApp = new NewApp;

// Static property can be accessed by it's instances
console.log('newApp.MULTIPLE_VERSIONS_SUPPORTED:', newApp.MULTIPLE_VERSIONS_SUPPORTED); // true

// Inheritance
class StandardApp extends NewApp {}

// Static property can be inherited
console.log('StandardApp.MULTIPLE_VERSIONS_SUPPORTED:', StandardApp.MULTIPLE_VERSIONS_SUPPORTED); // true

// Static property can be overwritten
StandardApp.MULTIPLE_VERSIONS_SUPPORTED = false;

const std = new StandardApp;

console.log('std.MULTIPLE_VERSIONS_SUPPORTED:', std.MULTIPLE_VERSIONS_SUPPORTED); // false


1
การเข้าถึงstaticฟิลด์โดยอินสแตนซ์จะค่อนข้างแปลกที่คุณจะไม่พูด? ในบางภาษาเช่น Java IDE จะออกคำเตือน / คำใบ้หากคุณทำอะไรแบบนั้น
Isac

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