Programming

ES6 클래스 변수 대안

procodes 2020. 2. 14. 23:52
반응형

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 = trueconstructor...하지만 난 더 큰 클래스의 20 ~ 30 + PARAMS이 특히 '정크'내 생성자에 싶지 않아요.

나는이 문제를 처리 할 수있는 많은 방법을 생각하고 있었지만 아직 좋은 것을 찾지 못했습니다. (예 : 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는 ES2015에 대해이 기능을 포함하지 않기로 명시 적으로 결정했습니다. 그러나 합의는 ES2016에 추가 할 것으로 보인다.

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

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

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

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


[긴 실, 이미 옵션으로 나열되어 있는지 확실하지 않습니다 ...].
간단한 대안 에 대한 contsants는 , 클래스의 const를 외부를 정의하는 것입니다. 이것은 getter와 함께 제공되지 않는 한 모듈 자체에서만 액세스 할 수 있습니다.
이 방법 prototype은 어지럽히 지 않으며을 얻습니다 const.

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

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에 넣었다.


ES7 클래스 멤버 구문 :

ES7생성자 함수를 '정크'하는 솔루션이 있습니다. 예를 들면 다음과 같습니다.

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

이 구문을 사용할 때이 구문이 모든 브라우저에서 지원되지 않을 수 있으며 이전 버전의 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);


이 문제를 해결하는 방법은 다른 옵션 (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(),
        });

    }
}

여전히 'fat'생성자로 끝나지 만 적어도 하나의 클래스에서 하나의 적중에 할당됩니다.

편집 # 2 : 이제 완전한 원이되었고 이제 생성자에 값을 할당하고 있습니다.

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

왜? 위의 내용과 일부 JSdoc 주석을 사용하여 PHPStorm은 속성에서 코드 완성을 수행 할 수있었습니다. 한 번의 클릭으로 모든 변수를 할당하는 것이 좋았지 만, 코드를 작성할 수없는 속성 인 imo는 (거의 확실하지 않은) 성능상의 이점이 없습니다.


생성자 안에서 변수를 선언 할 수 있습니다.

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

var foo = new Foo()

foo.method()

이것은 정적의 약간 hackish 콤보이며 나를 위해 일합니다.

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

강력한 리터럴과 더 큰 클로저에 포함 된 작은 템플릿 라이브러리 실행 라이브러리를 사용하여 전체 문제를 피할 수 있습니까?

현재 폐쇄를 무시하고

const myDynamicInputs=(items)=>\backtick -${ items.map((item, I, array)=>'${do tons of junk}').join('')}';

http://codepen.io/jfrazz/pen/BQJPBZ/

이것은 리포지토리에서 제공 할 수있는 가장 간단한 예입니다. 처음 400 줄은 데이터 라이브러리 + 기본 유틸리티 함수입니다. 또한 소수의 유틸리티 상수.

보일러 플레이트 (앱 사용자가 다운로드 한 데이터)로 전환 한 후에는 레이아웃 템플릿을 가져 와서 재배치해야하지만 입력, 드롭 다운 또는 52 페이지로 구성 할 수 있습니다. 질문과 데이터.

이것이 두 번째 예입니다. 객체를 먹고 다양한 유형의 입력을 얻습니다. 모두 const를 라이브러리의 기본 변수로 사용하여 구성됩니다.

http://codepen.io/jfrazz/pen/rWprVR/

정확히 당신이 요구 한 것이 아니라, 그 상수를 분명하게 보여주는 것이 역동적 일 수 있습니다.

참고 URL : https://stackoverflow.com/questions/22528967/es6-class-variable-alternatives



반응형