[javascript] ES6 클래스 변수 대안

현재 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어떻게 든 통합 하려고 생각했습니다 .)

이 상황을 처리하기 위해 어떤 종류의 아이디어가 필요합니까?



답변

2018 년 업데이트 :

이제 3 단계 제안이 있습니다.이 답변이 몇 달 안에 더 이상 사용되지 않기를 기대합니다.

그 동안 TypeScript 또는 babel을 사용하는 사람은 다음 구문을 사용할 수 있습니다.

varName = value

클래스 선언 / 표현식 본문 안에 변수를 정의합니다. 몇 달 / 주 안에 업데이트를 게시 할 수 있기를 바랍니다.

업데이트 : Chrome 74는 이제이 구문이 작동합니다.


ES6의 제안에 대한 ES 위키의 메모 ( 최대 클래스 ) :

프로토 타입 데이터 속성 (메소드 이외의) 클래스 속성 또는 인스턴스 속성을 정의하기위한 (의도적으로) 직접적인 선언적 방법은 없습니다

클래스 속성 및 프로토 타입 데이터 속성은 선언 외부에서 작성해야합니다.

클래스 정의에 지정된 속성에는 마치 객체 리터럴에 나타나는 것과 동일한 속성이 할당됩니다.

이것은 당신이 요구하는 것이 고려되고 명시 적으로 결정 되었음을 의미합니다 .

하지만 … 왜?

좋은 질문. TC39의 훌륭한 사람들은 클래스 선언이 클래스의 기능을 선언하고 정의하기를 원합니다. 회원이 아닙니다. ES6 클래스 선언은 사용자에 대한 계약을 정의합니다.

클래스 정의는 프로토 타입 메소드를 정의합니다. 프로토 타입에서 변수를 정의하는 것은 일반적으로 수행하는 작업이 아닙니다. 물론 다음을 사용할 수 있습니다.

constructor(){
    this.foo = bar
}

제안한 생성자에서. 또한 컨센서스 요약을 참조하십시오 .

ES7 이상

그에 근무중인 ES7에 대한 새로운 제안은 클래스 선언과 표현을 통해보다 간결 인스턴스 변수를 허용 – https://esdiscuss.org/topic/es7-property-initializers


답변

벤자민의 대답에 추가하기 위해 클래스 변수는 가능하지만 prototype설정하는 데는 사용하지 않습니다 .

진정한 클래스 변수의 경우 다음과 같은 작업을 수행하려고합니다.

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

클래스 메소드 내에서 변수에 this.constructor.foo(또는 MyClass.foo) 로 액세스 할 수 있습니다 .

이러한 클래스 속성은 일반적으로 클래스 인스턴스에서 액세스 할 수 없습니다. 즉 MyClass.foo제공 'bar'하지만 new MyClass().foo입니다undefined

인스턴스에서 클래스 변수에 액세스하려면 게터를 추가로 정의해야합니다.

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

MyClass.foo = 'bar';

나는 이것을 Traceur로만 테스트했지만 표준 구현에서 동일하게 작동한다고 생각합니다.

JavaScript에는 실제로 클래스가 없습니다 . ES6에서도 클래스 기반 언어가 아닌 객체 또는 프로토 타입 기반 언어를 찾고 있습니다. 어떤에서 function X () {}, X.prototype.constructor점에 백업 X. new연산자를 사용 하면 X상속되는 새 객체가 만들어 X.prototype집니다. 모든 정의되지 않은 (포함하는 새로운 객체의 속성 constructor) 거기에서 조회됩니다. 이것을 객체 및 클래스 속성을 생성하는 것으로 생각할 수 있습니다.


답변

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'


답변

귀하의 예에서 :

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'];}문자열이면 false 입니다. 이 경우 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


답변

문제는 주로 문체로 구성되어 있기 때문에 (생성 된 선언으로 생성자를 채우지 않으려는 경우) 문체 적으로 해결할 수 있습니다.

내가 보는 방식으로, 많은 클래스 기반 언어는 생성자가 클래스 이름 자체의 이름을 딴 함수입니다. 스타일 적으로 우리는 그것을 스타일에 따라 이해하는 ES6 클래스를 만들기 위해 사용할 수 있지만 생성자에서 발생하는 일반적인 동작을 우리가 수행하는 모든 속성 선언과 그룹화하지는 않습니다. 우리는 단순히 실제 JS 생성자를 “선언 영역”으로 사용하고, 그렇지 않으면 “다른 생성자 항목”영역으로 취급하는 함수라는 클래스를 만들어 실제 생성자의 끝에서 호출합니다.

"엄격한 사용";

MyClass 클래스
{
    // 속성을 선언 한 다음 this를 호출합니다 .ClassName (); 여기에서
    건설자(){
        this.prop1 = 'blah 1';
        this.prop2 = 'blah 2';
        this.prop3 = 'blah 3';
        this.MyClass ();
    }

    // 더 이상 선언과 혼동되지 않는 모든 종류의 다른 "생성자"항목
    내 수업() {
        무엇이든해라();
    }
}

새 인스턴스가 생성되면 둘 다 호출됩니다.

Sorta는 선언과 다른 생성자 작업을 분리하는 2 개의 생성자를 갖는 것과 같으며 문체 적으로 이해하기가 너무 어렵지 않습니다.

인스턴스화에서 발생해야하는 두 가지 선언 및 / 또는 많은 작업을 처리 할 때 사용하고 두 아이디어를 서로 구별되게 유지하려는 경우 사용하기에 좋은 스타일입니다.


참고 : 나는 매우 의도적으로 (AN처럼 “초기화”의 전형적인 관용적 아이디어를 사용하지 않는 init()또는 initialize()사람들은 종종 다르게 사용되기 때문에 방법). 구성과 초기화라는 아이디어에는 약간의 차이가 있습니다. 생성자와 함께 작업하면 사람들은 인스턴스화의 일부로 자동 호출된다는 것을 알고 있습니다. init많은 사람들이의 형태로 무언가를 수행해야한다고 생각 하는 방법을 많은 사람들이 한 번에 var mc = MyClass(); mc.init();알지 못합니다. 내가 클래스의 사용자에 대한 초기화 과정을 추가하려고 아니에요, 내가 추가하려고 해요 클래스 자체의 건설 과정.

어떤 사람들은 잠깐 동안 이중 복용을 할 수도 있지만 실제로는 약간의 요점입니다. 그것은 의도가 건설의 일부라는 것을 의사 소통합니다. 어떻게 ES6 생성자가 작동하는지 “를 확인하고 실제 생성자를보고”오, 그들은 맨 아래에서 호출합니다. 알겠습니다 “, 그 의도를 전달하지 않거나 잘못 전달하는 것보다 훨씬 낫습니다. 사람들이 그것을 잘못 사용하고 외부와 정크에서 초기화하려고합니다. 그것은 내가 제안하는 패턴에 매우 의도적입니다.


그 패턴을 따르고 싶지 않은 사람들에게는 그 반대도 효과가 있습니다. 처음에 선언을 다른 함수로 작성하십시오. “properties”또는 “publicProperties”또는 다른 이름으로 지정할 수 있습니다. 그런 다음 나머지는 일반 생성자에 넣습니다.

"엄격한 사용";

MyClass 클래스
{
    properties () {
        this.prop1 = 'blah 1';
        this.prop2 = 'blah 2';
        this.prop3 = 'blah 3';
    }

    constructor () {
        this.properties ();
        무엇이든해라();
    }
}

이 두 번째 방법은 더 깔끔해 보일 수 있지만 properties이 방법을 사용하는 한 클래스가 다른 클래스를 확장함에 따라 재정의 되는 고유 한 문제도 있습니다 . properties이를 피하려면 더 고유 한 이름 을 지정해야합니다. 첫 번째 방법에는 생성자의 가짜 절반이 클래스 이름을 따서 고유하게 지정 되므로이 문제가 없습니다.


답변

구식 방법은 어떻습니까?

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

생성자에서는 계산해야 할 변수 만 언급합니다. 이 기능에 대한 프로토 타입 상속을 좋아합니다. 많은 메모리를 절약 할 수 있습니다 (할당되지 않은 변수가 많은 경우).


답변

Benjamin이 자신의 답변에서 언급 한 것처럼 TC39는 ES ES에 대해이 기능을 포함하지 않기로 명시 적으로 결정했습니다. 그러나 합의는 ES2016에 추가 할 것으로 보인다.

구문은 아직 결정되지 않았지만 ES2016에 대한 클래스에 정적 속성을 선언 할 수 있는 예비 제안 이 있습니다.

babel의 마술 덕분에 오늘 이것을 사용할 수 있습니다. 이 지침 에 따라 클래스 속성 변환을 활성화하면 좋습니다. 구문의 예는 다음과 같습니다.

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

이 제안은 매우 초기 상태이므로 시간이 지남에 따라 구문을 조정할 수 있도록 준비하십시오.