ทางเลือกตัวแปรคลาส ES6


492

ปัจจุบันใน ES5 พวกเราหลายคนกำลังใช้รูปแบบต่อไปนี้ในกรอบงานเพื่อสร้างคลาสและตัวแปรคลาสซึ่งสะดวกสบาย:

// ES 5
FrameWork.Class({

    variable: 'string',
    variable2: true,

    init: function(){

    },

    addItem: function(){

    }

});

ใน ES6 คุณสามารถสร้างคลาสได้ แต่ไม่มีตัวเลือกให้มีคลาสตัวแปร:

// ES6
class MyClass {
    const MY_CONST = 'string'; // <-- this is not possible in ES6
    constructor(){
        this.MY_CONST;
    }
}

น่าเสียดายที่ข้อความข้างต้นใช้งานไม่ได้เนื่องจากคลาสเท่านั้นที่สามารถมีวิธีได้

ฉันเข้าใจว่าฉันสามารถthis.myVar = trueในconstructor... แต่ฉันไม่ต้องการที่จะ 'ขยะ' คอนสตรัคของฉันโดยเฉพาะอย่างยิ่งเมื่อฉันมี 20-30 + พารามิเตอร์สำหรับการเรียนที่ใหญ่กว่า

ฉันคิดหลายวิธีในการจัดการปัญหานี้ แต่ยังไม่พบสิ่งที่ดี (ตัวอย่างเช่น: สร้างClassConfigจัดการและผ่านparameterวัตถุซึ่งมีการประกาศแยกต่างหากจากชั้นเรียนแล้วจัดการจะแนบไปกับชั้นเรียนผมคิดเกี่ยวกับ.. WeakMapsยังจะบูรณาการอย่างใด.)

คุณจะต้องมีแนวคิดอะไรในการรับมือกับสถานการณ์นี้?


1
ปัญหาหลักของคุณคือคุณจะมีการซ้ำซ้อนของคอนthis.member = memberสตรัคเตอร์ด้วยพารามิเตอร์ 20-30 หรือไม่?
ΘεόφιλοςΜουρατίδης

1
คุณไม่สามารถใช้งานpublic variable2 = trueภายใต้คลาสได้หรือไม่ นี้จะกำหนดไว้ในต้นแบบ

14
@ ΘεόφιλοςΜουρατίδης: ใช่และฉันต้องการใช้ตัวสร้างของฉันสำหรับขั้นตอนการเริ่มต้นไม่ใช่สำหรับการประกาศตัวแปร
wintercounter

@derylius: นี่เป็นปัญหาหลักมันไม่มีคุณสมบัติดังกล่าว แม้แต่การใช้งานสาธารณะ / ส่วนตัวยังไม่ได้ตัดสินใจในร่าง ES6 ทดสอบการหมุน: es6fiddle.net
wintercounter

ตามล่าสุดมันมีฟังก์ชั่นนี้: wiki.ecmascript.org/doku.php?id=harmony:classes

คำตอบ:


515

ปรับปรุง 2018:

ขณะนี้มีข้อเสนอ 3 ขั้นตอน - ฉันรอคอยที่จะทำให้คำตอบนี้ล้าสมัยในไม่กี่เดือน

ในระหว่างนี้ผู้ใช้ TypeScript หรือ babel สามารถใช้ไวยากรณ์ได้:

varName = value

ภายในคลาสการประกาศ / การแสดงออกของร่างกายและมันจะกำหนดตัวแปร หวังว่าในอีกไม่กี่เดือน / สัปดาห์ฉันจะสามารถโพสต์การอัปเดตได้

อัปเดต: Chrome 74 มาพร้อมกับไวยากรณ์นี้ใช้งานได้แล้ว


หมายเหตุในวิกิ ES สำหรับข้อเสนอใน ES6 ( คลาสที่เล็กที่สุด ):

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

คุณสมบัติคลาสและคุณสมบัติข้อมูลต้นแบบต้องสร้างขึ้นนอกการประกาศ

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

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

แต่ ... ทำไม

คำถามที่ดี. คนดีของ TC39 ต้องการประกาศคลาสเพื่อประกาศและกำหนดความสามารถของคลาส ไม่ใช่สมาชิก การประกาศคลาส ES6 กำหนดสัญญาสำหรับผู้ใช้

โปรดจำไว้ว่าคำจำกัดความของคลาสจะกำหนดวิธีการต้นแบบ - การกำหนดตัวแปรในต้นแบบไม่ใช่สิ่งที่คุณทำ แน่นอนคุณสามารถใช้:

constructor(){
    this.foo = bar
}

ในตัวสร้างแบบที่คุณแนะนำ ยังเห็นบทสรุปของฉันทามติที่

ES7 ขึ้นไป

ข้อเสนอใหม่สำหรับ ES7 กำลังดำเนินการซึ่งอนุญาตให้ตัวแปรอินสแตนซ์ที่กระชับมากขึ้นผ่านการประกาศคลาสและนิพจน์ - https://esdiscuss.org/topic/es7-property-initializers


4
@ วินเทอร์พบสิ่งที่สำคัญที่ต้องทำคือการอนุญาตให้มีการกำหนดคุณสมบัติจะกำหนดไว้ในต้นแบบเช่นวิธีการและไม่ใช่ในแต่ละอินสแตนซ์ คลาสที่น้อยที่สุดยังคงอยู่ที่การสืบทอดต้นแบบที่สำคัญมาก สิ่งที่คุณต้องการทำในกรณีของคุณคือโครงสร้างการแชร์และมอบหมายสมาชิกสำหรับแต่ละอินสแตนซ์ นี่ไม่ใช่สิ่งที่คลาสใน ES6 มีจุดประสงค์เพื่อการใช้งานร่วมกัน ใช่แล้วสำหรับโครงสร้างการแชร์คุณต้องยึดติดกับไวยากรณ์เก่า อย่างน้อยก็จนถึง ES7 :)
Benjamin Gruenbaum

5
คุณอาจต้องการพูดถึงstaticคุณสมบัติ
Bergi

8
โอ๊ะสมองผายลม ฉันลืมว่ามันstaticใช้ได้เฉพาะกับวิธีการเช่นกัน
Bergi

637
บางทีคนที่ดีใน TC39 ควรตั้งชื่อแนวคิดนี้เป็นอย่างอื่นที่ไม่ใช่ "คลาส" หากพวกเขาไม่ต้องการให้มันทำตัวเหมือนคนอื่น ๆ ในโลกแห่งการเขียนโปรแกรมที่คาดหวังจากสิ่งที่เรียกว่า "คลาส"
Alex

7
ดูเพิ่มเติมข้อเสนอ "คลาสฟิลด์และคุณสมบัติคงที่" (ใช้งานแล้วในBabel : github.com/jeffmo/es-class-fields-and-static-properties
Matt Browne

127

เพียงเพิ่มคำตอบของเบนจามิน - ตัวแปรระดับเป็นไปได้ แต่คุณจะไม่ใช้prototypeเพื่อตั้งค่า

สำหรับตัวแปรคลาสจริงคุณต้องการทำสิ่งต่อไปนี้:

class MyClass {}
MyClass.foo = 'bar';

จากภายในวิธีการเรียนที่ตัวแปรสามารถเข้าถึงได้เป็นthis.constructor.foo(หรือMyClass.foo)

คุณสมบัติคลาสเหล่านี้มักจะไม่สามารถเข้าถึงได้จากอินสแตนซ์ของคลาส คือMyClass.fooให้'bar'แต่new MyClass().fooเป็นundefined

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

class MyClass {
    get foo() {
        return this.constructor.foo;
    }
}

MyClass.foo = 'bar';

ฉันได้ทดสอบสิ่งนี้กับ Traceur เท่านั้น แต่ฉันเชื่อว่ามันจะทำงานได้เหมือนกันในการติดตั้งแบบมาตรฐาน

จาวาสคริปต์ไม่ได้จริงๆต้องเรียน แม้จะใช้ ES6 เรากำลังมองหาภาษาเชิงวัตถุหรือต้นแบบมากกว่าภาษาที่ใช้ในระดับเดียวกัน ในการใด ๆfunction X () {}, จุดกลับไปX.prototype.constructor Xเมื่อnewผู้ประกอบการที่ใช้ในวัตถุใหม่จะถูกสร้างสืบทอดX X.prototypeใด ๆที่ไม่ได้กำหนดคุณสมบัติในการที่วัตถุใหม่ (รวมconstructor) จะเงยหน้าขึ้นมาจากที่นั่น เราสามารถคิดสิ่งนี้เป็นการสร้างคุณสมบัติของวัตถุและคลาส


29

Babelรองรับตัวแปรคลาสใน ESNext ให้ตรวจสอบตัวอย่างนี้:

class Foo {
  bar = 2
  static iha = 'string'
}

const foo = new Foo();
console.log(foo.bar, foo.iha, Foo.bar, Foo.iha);
// 2, undefined, undefined, 'string'

1
คุณสามารถทำให้แถบเป็นตัวแปรคลาสส่วนตัวโดยทำท่าด้วย "#" เช่นนี้: #bar = 2;
Lonnie Best

26

ในตัวอย่างของคุณ:

class MyClass {
    const MY_CONST = 'string';
    constructor(){
        this.MY_CONST;
    }
}

เนื่องจาก MY_CONST เป็นแบบดั้งเดิมhttps://developer.mozilla.org/en-US/docs/Glossary/Primitiveเราสามารถทำได้:

class MyClass {
    static get MY_CONST() {
        return 'string';
    }
    get MY_CONST() {
        return this.constructor.MY_CONST;
    }
    constructor() {
        alert(this.MY_CONST === this.constructor.MY_CONST);
    }
}
alert(MyClass.MY_CONST);
new MyClass

// alert: string ; true

แต่ถ้าMY_CONSTเป็นประเภทการอ้างอิงเช่นstatic get MY_CONST() {return ['string'];}การส่งออกการแจ้งเตือนเป็นสตริงเท็จ ในกรณีดังกล่าวdeleteผู้ประกอบการสามารถทำเคล็ดลับ:

class MyClass {
    static get MY_CONST() {
        delete MyClass.MY_CONST;
        return MyClass.MY_CONST = 'string';
    }
    get MY_CONST() {
        return this.constructor.MY_CONST;
    }
    constructor() {
        alert(this.MY_CONST === this.constructor.MY_CONST);
    }
}
alert(MyClass.MY_CONST);
new MyClass

// alert: string ; true

และในที่สุดสำหรับตัวแปรระดับไม่ได้const:

class MyClass {
    static get MY_CONST() {
        delete MyClass.MY_CONST;
        return MyClass.MY_CONST = 'string';
    }
    static set U_YIN_YANG(value) {
      delete MyClass.MY_CONST;
      MyClass.MY_CONST = value;
    }
    get MY_CONST() {
        return this.constructor.MY_CONST;
    }
    set MY_CONST(value) {
        this.constructor.MY_CONST = value;
    }
    constructor() {
        alert(this.MY_CONST === this.constructor.MY_CONST);
    }
}
alert(MyClass.MY_CONST);
new MyClass
// alert: string, true
MyClass.MY_CONST = ['string, 42']
alert(MyClass.MY_CONST);
new MyClass
// alert: string, 42 ; true

5
กรุณาหลีกเลี่ยงการdeleteดำเนินการถ้าเพียงอย่างเดียวด้วยเหตุผลด้านประสิทธิภาพ Object.definePropertyสิ่งที่คุณต้องการจริงที่นี่คือ
Bergi

@ shinzou ฉันคิดในสิ่งเดียวกัน ไม่จำเป็นต้องเป็นความผิดของ OP แต่; เป็นภาษาที่ขาดกลไกที่เหมาะสมในการแสดงข้อมูลในแบบที่สะท้อนถึงความสัมพันธ์ในโลกแห่งความเป็นจริง
user1974458

17

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

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

"ใช้อย่างเข้มงวด";

คลาส MyClass
{
    // ประกาศคุณสมบัติของคุณเท่านั้นแล้วเรียก thisClassName (); จากที่นี่
    คอนสตรัค () {
        this.prop1 = 'blah 1';
        this.prop2 = 'blah 2';
        this.prop3 = 'blah 3';
        this.MyClass ();
    }

    // ทุกสิ่ง "คอนสตรัคเตอร์" อื่น ๆ ทุกประเภทไม่สับสนกับการประกาศอีกต่อไป
    ห้องเรียนของฉัน() {
        ทำอะไรก็ได้();
    }
}

ทั้งสองจะถูกเรียกว่าเป็นตัวอย่างใหม่ที่ถูกสร้างขึ้น

Sorta เหมือนกับการมี 2 constructors ซึ่งคุณแยกการประกาศและการดำเนินการ constructor อื่น ๆ ที่คุณต้องการและ stylistically ทำให้ไม่ยากเกินกว่าที่จะเข้าใจนั่นคือสิ่งที่เกิดขึ้น

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


หมายเหตุ : ฉันตั้งใจจะไม่ใช้ความคิดที่เป็นสำนวนทั่วไปของ "การเริ่มต้น" (เช่นinit()หรือinitialize()วิธีการ) เพราะพวกเขามักจะใช้ที่แตกต่างกัน มีความแตกต่างระหว่างความคิดในการสร้างและการเริ่มต้นสันนิษฐานว่ามีความแตกต่าง การทำงานกับคอนสตรัคเตอร์คนรู้ว่าพวกเขาถูกเรียกโดยอัตโนมัติเป็นส่วนหนึ่งของการสร้างอินสแตนซ์ การเห็นinitวิธีการที่หลายคนกำลังคาดเดาโดยไม่ต้องมองแวบเดียวว่าพวกเขาต้องทำอะไรบางอย่างในรูปแบบของvar mc = MyClass(); mc.init();เพราะนั่นคือวิธีที่คุณมักจะเริ่มต้น ฉันไม่ได้พยายามที่จะเพิ่มการเริ่มต้นสำหรับผู้ใช้ของชั้นที่ฉันพยายามที่จะเพิ่มการดำเนินการก่อสร้างของชั้นเอง

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


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

"ใช้อย่างเข้มงวด";

คลาส MyClass
{
    คุณสมบัติ() {
        this.prop1 = 'blah 1';
        this.prop2 = 'blah 2';
        this.prop3 = 'blah 3';
    }

    ตัวสร้าง () {
        this.properties ();
        ทำอะไรก็ได้();
    }
}

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


1
กรุณาอย่าใช้วิธีต้นแบบตั้งชื่อตามชั้นเรียนของตัวเอง นั่นไม่ใช่สำนวนใน JS อย่าพยายามทำให้มันดูเหมือนภาษาอื่น initถ้าคุณอยากที่จะใช้วิธีการนี้ชื่อบัญญัติสำหรับวิธีการคือ
Bergi

1
@Bergi - เป็นรูปแบบที่ใช้บ่อยและมักจะหมายถึงการถูกเรียกจากนอกชั้นเรียนเมื่อผู้ใช้ต้องการที่จะออกไปข้างนอกเริ่มต้นคือinit var b = new Thing(); b.init();นี่เป็นตัวเลือกโวหาร 100% ที่ฉันต้องการสื่อสารว่าเป็นฟังก์ชั่นที่ 2 ที่ถูกเรียกโดยอัตโนมัติโดยใช้ประโยชน์จากรูปแบบที่พบในภาษาอื่น มีความเป็นไปได้น้อยกว่าที่บางคนจะมองดูสิ่งนี้และคิดว่าพวกเขาจำเป็นต้องเรียกใช้MyClassวิธีการจากภายนอกมีแนวโน้มว่าพวกเขาจะตระหนักถึงความตั้งใจเป็นวิธีที่สองที่ทำหน้าที่ในการก่อสร้าง
Jimbo Jonny

หืมมมผมอาจจะตระหนักว่าโดยมองหาที่สร้าง MyClassแต่ไม่ได้มาจากชื่อวิธี
Bergi

1
@Bergi - การใช้รูปแบบจากภาษาอื่นและนำไปใช้กับ JS ในแบบที่ไม่ใช่ในทางเทคนิคว่าเกิดอะไรขึ้น แต่ก็ยังใช้งานได้ไม่สมบูรณ์หากไม่มีแบบอย่าง คุณไม่สามารถบอกฉันได้ว่าไม่มีใครสังเกตเห็นว่า$myvarวิธีมาตรฐานในการอ้างถึงตัวแปรที่ตั้งใจจะเก็บออบเจ็กต์ jQuery นั้นไม่เหมือนกับรูปแบบของการมี $ ที่จุดเริ่มต้นของตัวแปรที่โปรแกรมเมอร์ PHP จำนวนมากคุ้นเคย เพียงนัยเล็กน้อยที่ "ใช่มันไม่เหมือนกันแน่นอน ... แต่ดู ... มันยังคงเป็นตัวแปรเพราะนั่นเป็นวิธีที่ตัวแปรจะทำในบางภาษา!" จะช่วยให้
Jimbo Jonny

1
ฉันชอบตัวเลือกสุดท้ายของคุณด้วยproperties()ฟังก์ชั่น อย่างไรก็ตามสิ่งต่าง ๆ มีความซับซ้อนมากขึ้นเล็กน้อยเมื่อขยายคลาสดังนั้นฉันขอแนะนำว่าถ้าคุณใช้properties()ฟังก์ชันในคลาสทั้งหมดของคุณเพื่อประกาศคุณสมบัติคลาสที่คุณนำหน้าชื่อเมธอดด้วยชื่อคลาส (IE: MyClassProperties()เพื่อหลีกเลี่ยงการแทนที่โดยบังเอิญ ฟังก์ชั่นการโทรภายในคลาสย่อยนอกจากนี้โปรดทราบว่าการประกาศใด ๆ ที่จะsuper()ต้องประกาศก่อนในตัวสร้างคลาส
TheDarkIn1978

14

แล้วทางโรงเรียนเก่าล่ะ?

class MyClass {
     constructor(count){ 
          this.countVar = 1 + count;
     }
}
MyClass.prototype.foo = "foo";
MyClass.prototype.countVar = 0;

// ... 

var o1 = new MyClass(2); o2 = new MyClass(3);
o1.foo = "newFoo";

console.log( o1.foo,o2.foo);
console.log( o1.countVar,o2.countVar);

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


4
สิ่งนี้ไม่ได้บันทึกหน่วยความจำและมันทำให้ประสิทธิภาพแย่ลงกว่าที่คุณประกาศไว้ในตัวสร้าง codereview.stackexchange.com/a/28360/9258
Esailija

2
นอกจากนี้ยังเป็นการทำลายวัตถุประสงค์ของการใช้คลาส ES6 ตั้งแต่แรก ดูเหมือนว่าแทบจะเป็นไปไม่ได้เลยที่จะใช้คลาส ES6 เช่นคลาส oop จริงซึ่งค่อนข้างน่าผิดหวัง ...
Kokodoko

4
@Kokodoko oop จริง - คุณหมายถึงเช่นใน Java? ฉันเห็นด้วยฉันสังเกตเห็นว่าผู้คนจำนวนมากโกรธ JS เพราะพวกเขาพยายามที่จะใช้มันเหมือน Java อย่างที่พวกเขาคุ้นเคยเพราะไวยากรณ์ของ JS ดูเหมือนกันและพวกเขาคิดว่ามันทำงานในลักษณะเดียวกัน ... แต่จาก ข้างในมันค่อนข้างแตกต่างอย่างที่เรารู้
zarkone

2
มันไม่ได้เป็นเพียงเกี่ยวกับไวยากรณ์ภาษา ปรัชญา OOP ให้ความสำคัญกับการเขียนโปรแกรมโดยทั่วไปโดยเฉพาะเมื่อสร้างแอพและเกม JS มีการใช้งานแบบดั้งเดิมในการสร้างหน้าเว็บที่แตกต่างกันมาก อย่างไรก็ตามทั้งสองวิธีนั้นจำเป็นต้องมารวมกันเนื่องจากเว็บกำลังกลายเป็นแอพพลิเคชั่นมากขึ้นเช่นกัน
Kokodoko

1
@Kokodoko เพียงเพราะมันเป็นความแตกต่างกัน OOP ไม่ได้หมายความว่ามันไม่ได้เป็น OOP ต้นแบบเป็นวิธี OOP ที่แท้จริง 100% ไม่มีใครจะเรียกตนเองว่า "non-OOP" เพราะมันใช้ต้นแบบ การไม่จับคู่กระบวนทัศน์ของ OOP อื่นหมายความว่า: มันแตกต่างกัน
Dave Newton

13

ดังที่เบนจามินกล่าวในคำตอบของเขา TC39 ตัดสินใจอย่างชัดเจนว่าจะไม่รวมคุณลักษณะนี้อย่างน้อยสำหรับ ES2015 อย่างไรก็ตามฉันทามติดูเหมือนว่าพวกเขาจะเพิ่มใน ES2016

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

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

class foo {
  static myProp = 'bar'
  someFunction() {
    console.log(this.myProp)
  }
}

ข้อเสนอนี้อยู่ในสถานะเริ่มต้นดังนั้นโปรดเตรียมไวยากรณ์ของคุณเมื่อเวลาผ่านไป


ใช่นั่นไม่ใช่ ES6 คุณไม่ควรใช้มันจนกว่าคุณจะรู้ว่ามันคืออะไร
Bergi

10

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

// will be accessible only from the module itself
const MY_CONST = 'string'; 
class MyClass {

    // optional, if external access is desired
    static get MY_CONST(){return MY_CONST;}

    // access example
    static someMethod(){
        console.log(MY_CONST);
    }
}

จะเกิดอะไรขึ้นถ้าคุณก) ใช้varแทนconst2) ใช้หลายอินสแตนซ์ของคลาสนี้? จากนั้นตัวแปรภายนอกจะเปลี่ยนไปตามแต่ละตัวอย่างของคลาส
nick carraway

1
fail point @nickcarraway ข้อเสนอของฉันย่อมาจาก const เท่านั้นไม่ใช่เป็นคำถามที่มีชื่อว่า
Hertzel Guinness

6

ES7 ไวยากรณ์สมาชิกคลาส:

ES7มีทางออกสำหรับ 'junking' ฟังก์ชั่นคอนสตรัคของคุณ นี่คือตัวอย่าง:

class Car {
  
  wheels = 4;
  weight = 100;

}

const car = new Car();
console.log(car.wheels, car.weight);

ตัวอย่างข้างต้นจะมีลักษณะดังต่อไปนี้ในES6:

class Car {

  constructor() {
    this.wheels = 4;
    this.weight = 100;
  }

}

const car = new Car();
console.log(car.wheels, car.weight);

โปรดใช้ความระมัดระวังเมื่อใช้สิ่งนี้เพื่อให้เบราว์เซอร์ทั้งหมดอาจไม่สนับสนุนเบราว์เซอร์นี้และอาจต้องมีการ transpiled JS เวอร์ชันก่อนหน้า

โบนัส: โรงงานผลิตวัตถุ:

function generateCar(wheels, weight) {

  class Car {

    constructor() {}

    wheels = wheels;
    weight = weight;

  }

  return new Car();

}


const car1 = generateCar(4, 50);
const car2 = generateCar(6, 100);

console.log(car1.wheels, car1.weight);
console.log(car2.wheels, car2.weight);


3
คุณได้ทำอินสแตนซ์ตัวแปรสำเร็จไม่ใช่ตัวแปรคลาส
Tjad Clark

5

คุณสามารถเลียนแบบพฤติกรรมของคลาส es6 ... และใช้ตัวแปรคลาสของคุณ :)

ดูแม่ ... ไม่มีคลาส!

// Helper
const $constructor = Symbol();
const $extends = (parent, child) =>
  Object.assign(Object.create(parent), child);
const $new = (object, ...args) => {
  let instance = Object.create(object);
  instance[$constructor].call(instance, ...args);
  return instance;
}
const $super = (parent, context, ...args) => {
  parent[$constructor].call(context, ...args)
}
// class
var Foo = {
  classVariable: true,

  // constructor
  [$constructor](who){
    this.me = who;
    this.species = 'fufel';
  },

  // methods
  identify(){
    return 'I am ' + this.me;
  }
}

// class extends Foo
var Bar = $extends(Foo, {

  // constructor
  [$constructor](who){
    $super(Foo, this, who);
    this.subtype = 'barashek';
  },

  // methods
  speak(){
    console.log('Hello, ' + this.identify());
  },
  bark(num){
    console.log('Woof');
  }
});

var a1 = $new(Foo, 'a1');
var b1 = $new(Bar, 'b1');
console.log(a1, b1);
console.log('b1.classVariable', b1.classVariable);

ฉันวางไว้บนGitHub


0

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

function MyClassFields(){
    this.createdAt = new Date();
}

MyClassFields.prototype = {
    id : '',
    type : '',
    title : '',
    createdAt : null,
};

class MyClass {
    constructor() {
        $.extend(this,new MyClassFields());
    }
};

- อัปเดตความคิดเห็นการติดตามของ Bergi

ไม่มีรุ่น JQuery:

class SavedSearch  {
    constructor() {
        Object.assign(this,{
            id : '',
            type : '',
            title : '',
            createdAt: new Date(),
        });

    }
}

คุณยังท้ายด้วยตัวสร้าง 'อ้วน' แต่อย่างน้อยมันทั้งหมดในคลาสเดียวและมอบหมายในหนึ่งครั้ง

แก้ไข # 2: ตอนนี้ฉันไปครบวงจรแล้วตอนนี้ฉันกำลังกำหนดค่าในนวกรรมิกเช่น

class SavedSearch  {
    constructor() {
        this.id = '';
        this.type = '';
        this.title = '';
        this.createdAt = new Date();
    }
}

ทำไม? เรียบง่ายจริงๆโดยใช้ด้านบนบวกกับความคิดเห็นบางส่วนของ JSdoc PHPStorm ก็สามารถทำโค้ดให้สมบูรณ์ในคุณสมบัติ การกำหนด vars ทั้งหมดในการเข้าชมครั้งเดียวเป็นสิ่งที่ดี แต่การไม่สามารถเขียนโค้ดคุณสมบัติให้เสร็จสมบูรณ์นั้นไม่คุ้มค่ากับประสิทธิภาพการทำงาน (เกือบจะแน่นอนน้อยที่สุด)


2
หากคุณสามารถทำไวยากรณ์คุณยังสามารถทำclass Object.assignไม่จำเป็นต้องใช้ jQuery
Bergi

ฉันไม่เห็นจุดใด ๆ ที่จะสร้างคอนMyClassFieldsสตรัคเตอร์ - มันไม่มีวิธีการใด ๆ คุณช่วยอธิบายได้ไหมว่าทำไมคุณถึงทำแบบนี้ (แทนที่จะพูดฟังก์ชั่นพื้นฐานอย่างง่าย ๆ ที่คืนค่าตัวอักษรวัตถุ)?
Bergi

@Bergi - อาจจะเป็นนิสัยมากกว่าสิ่งอื่นใดที่จะซื่อสัตย์ ยังเรียกที่ดีเกี่ยวกับ Object.assign - ไม่รู้เกี่ยวกับเรื่องนั้น!
Steve Childs

0

คุณสามารถประกาศตัวแปรภายใน Constructor ได้

class Foo {
    constructor() {
        var name = "foo"
        this.method = function() {
            return name
        }
    }
}

var foo = new Foo()

foo.method()

1
คุณจะต้องสร้างตัวอย่างของคลาสนี้เพื่อใช้ตัวแปรนี้ ฟังก์ชั่นคงที่ไม่สามารถเข้าถึงตัวแปรนั้นได้
Aditya

0

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

class foo{
   constructor(){
     //decalre your all variables
     this.MY_CONST = 3.14;
     this.x = 5;
     this.y = 7;
     // or call another method to declare more variables outside from constructor.
     // now create method level object reference and public level property
     this.MySelf = this;
     // you can also use var modifier rather than property but that is not working good
     let self = this.MySelf;
     //code ......... 
   }
   set MySelf(v){
      this.mySelf = v;
   }
   get MySelf(v){
      return this.mySelf;
   }
   myMethod(cd){
      // now use as object reference it in any method of class
      let self = this.MySelf;
      // now use self as object reference in code
   }
}

-1

นี่เป็นคำสั่งผสมที่คงที่และทำให้ฉันได้ผล

class ConstantThingy{
        static get NO_REENTER__INIT() {
            if(ConstantThingy._NO_REENTER__INIT== null){
                ConstantThingy._NO_REENTER__INIT = new ConstantThingy(false,true);
            }
            return ConstantThingy._NO_REENTER__INIT;
        }
}

อื่น ๆ ที่ใช้

var conf = ConstantThingy.NO_REENTER__INIT;
if(conf.init)...

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