[javascript] TypeScript에서 개인 키워드와 개인 필드의 차이점은 무엇입니까?

TypeScript 3.8 이상에서 private키워드를 사용하여 멤버를 비공개로 표시하는 것의 차이점은 무엇입니까?

class PrivateKeywordClass {
    private value = 1;
}

그리고 JavaScript를 위해 제안 된# 개인 필드를 사용합니다 :

class PrivateFieldClass {
    #value = 1;
}

다른 것을 선호해야합니까?



답변

비공개 키워드

TypeScript 의 개인 키워드컴파일 시간 주석입니다. 컴파일러에게 속성은 해당 클래스 내에서만 액세스 할 수 있어야한다고 알려줍니다.

class PrivateKeywordClass {
    private value = 1;
}

const obj = new PrivateKeywordClass();
obj.value // compiler error: Property 'value' is private and only accessible within class 'PrivateKeywordClass'.

그러나 컴파일 시간 검사는 예를 들어 유형 정보를 캐스트하여 쉽게 무시할 수 있습니다.

const obj = new PrivateKeywordClass();
(obj as any).value // no compile error

private키워드는 런타임에 적용되지 않습니다

방출 된 JavaScript

TypeScript를 JavaScript로 컴파일 할 때 private키워드는 간단히 제거됩니다.

class PrivateKeywordClass {
    private value = 1;
}

된다 :

class PrivateKeywordClass {
    constructor() {
        this.value = 1;
    }
}

이를 통해 private키워드가 런타임 보호 기능을 제공하지 않는 이유를 확인할 수 있습니다 . 생성 된 JavaScript에서는 일반 JavaScript 특성 일뿐입니다.

개인 분야

개인 필드 는 속성이 런타임에 개인용으로 유지되도록 합니다 .

class PrivateFieldClass {
    #value = 1;

    getValue() { return this.#value; }
}

const obj = new PrivateFieldClass();

// You can't access '#value' outside of class like this
obj.value === undefined // This is not the field you are looking for.
obj.getValue() === 1 // But the class itself can access the private field!

// Meanwhile, using a private field outside a class is a runtime syntax error:
obj.#value

// While trying to access the private fields of another class is 
// a runtime type error:
class Other {
    #value;

    getValue(obj) {
        return obj.#value // TypeError: Read of private field #value from an object which did not contain the field
    }
}

new Other().getValue(new PrivateKeywordClass());

클래스 외부의 개인 필드를 사용하려고하면 TypeScript에서 컴파일 시간 오류를 출력합니다.

개인 필드 액세스 오류

비공개 필드는 JavaScript 제안서 에서 제공되며 일반 JavaScript에서도 작동합니다.

방출 된 JavaScript

당신이 타이프에서 개인 필드를 사용하고 같은 당신의 출력을위한 자바 스크립트의 이전 버전을 대상으로하는 경우 es6또는 es2018, 타이프 라이터는 민간 분야의 런타임 동작을 에뮬레이트 코드를 생성하려고합니다

class PrivateFieldClass {
    constructor() {
        _x.set(this, 1);
    }
}
_x = new WeakMap();

를 타겟팅하는 esnext경우 TypeScript는 개인 필드를 생성합니다.

class PrivateFieldClass {
    constructor() {
        this.#x = 1;
    }
    #x;
}

어느 것을 사용해야합니까?

그것은 당신이 달성하려는 것에 달려 있습니다.

private키워드는 미세 기본이다. TypeScript 개발자가 수년 동안 달성하도록 설계되었으며 성공적으로 사용되었습니다. 기존 코드베이스가있는 경우 개인 필드를 사용하기 위해 모든 코드를 전환 할 필요는 없습니다. esnextTS가 개인 필드에 대해 방출하는 JS가 성능에 영향을 줄 수 있으므로 타겟팅하지 않는 경우 특히 그렇습니다 . 또한 비공개 입력란에는 private키워드 와 다른 미묘하지만 중요한 차이점이 있습니다.

그러나 런타임 개인 정보를 적용해야하거나 esnextJavaScript 를 출력하는 경우 개인 필드를 사용해야합니다.

또한 JavaScript / TypeScript 에코 시스템 내에서 개인 필드가 더 널리 보급됨에 따라 하나 또는 다른 것을 사용하는 조직 / 커뮤니티 규칙도 발전 할 것입니다.

노트의 다른 차이점

  • 개인 필드는 Object.getOwnPropertyNames비슷한 방법으로 반환되지 않습니다

  • 개인 필드는 JSON.stringify

  • 상속과 관련하여 중요한 경우가 있습니다.

    예를 들어 TypeScript는 수퍼 클래스의 개인 속성과 이름이 같은 서브 클래스에서 개인 속성을 선언하는 것을 금지합니다.

    class Base {
        private value = 1;
    }
    
    class Sub extends Base {
        private value = 2; // Compile error:
    }
    

    개인 필드에는 해당되지 않습니다.

    class Base {
        #value = 1;
    }
    
    class Sub extends Base {
        #value = 2; // Not an error
    }
    
  • private이니셜 라이저가없는 키워드 사유 재산이 방출되는 자바 스크립트의 속성 선언을 생성하지 않습니다 :

    class PrivateKeywordClass {
        private value?: string;
        getValue() { return this.value; }
    }
    

    컴파일 :

    class PrivateKeywordClass {
        getValue() { return this.value; }
    }
    

    개인 필드는 항상 속성 선언을 생성하는 반면 :

    class PrivateKeywordClass {
        #value?: string;
        getValue() { return this.#value; }
    }
    

    (타겟팅시 esnext)로 컴파일 :

    class PrivateKeywordClass {
        #value;
        getValue() { return this.#value; }
    }
    

더 읽을 거리 :


답변

사용 사례 : #-비공개 필드

머리말:

컴파일 타임 런타임 개인 정보

#-private 필드는 “해킹 가능하지 않은” 컴파일 타임 런타임 개인 정보를 제공합니다. 그것은 어떤 방식 으로든 클래스 본문 외부에서 멤버에 액세스하는 것을 방지하는 메커니즘 입니다.

class A {
    #a: number;
    constructor(a: number) {
        this.#a = a;
    }
}

let foo: A = new A(42);
foo.#a; // error, not allowed outside class bodies
(foo as any).#bar; // still nope.

안전한 클래스 상속

#개인 필드는 고유 한 범위를 갖습니다. 클래스 계층은 이름이 같은 개인 속성을 실수로 덮어 쓰지 않고도 구현할 수 있습니다.

class A {
    #a = "a";
    fnA() { return this.#a; }
}

class B extends A {
    #a = "b";
    fnB() { return this.#a; }
}

const b = new B();
b.fnA(); // returns "a" ; unique property #a in A is still retained
b.fnB(); // returns "b"

private속성이 덮어 쓰기 될 위험이있는 경우 TS 컴파일러에서 다행히 오류가 발생합니다 ( 이 예 참조 ). 그러나 컴파일 타임 기능의 특성으로 인해 컴파일 오류가 무시되고 및 / 또는 생성 된 JS 코드가 사용되는 경우 런타임에 모든 것이 여전히 가능합니다.

외부 라이브러리

라이브러리 작성자는 #클라이언트에 큰 변화를주지 않으면 서 개인 식별자를 리팩터링 할 수 있습니다. 다른 쪽의 라이브러리 사용자는 내부 필드에 액세스하지 못하도록 보호됩니다.

JS API가 개인 #필드를 생략 함

내장 JS 함수 및 메소드는 개인 #필드를 무시 합니다. 이로 인해 런타임시보다 예측 가능한 속성을 선택할 수 있습니다. 예 : Object.keys, Object.entries, JSON.stringify, for..in루프 등 ( 코드 샘플 , 또한 매트 Bierner의 참조 대답을 ) :

class Foo {
    #bar = 42;
    baz = "huhu";
}

Object.keys(new Foo()); // [ "baz" ]

사용 사례 : private키워드

머리말:

내부 클래스 API 및 상태에 대한 액세스 (컴파일 타임 전용 프라이버시)

private클래스의 멤버는 런타임에 일반적인 속성입니다. 이 유연성을 사용하여 클래스 내부 API 또는 외부에서 상태에 액세스 할 수 있습니다. 컴파일러 검사를 만족시키기 위해 형식 어설 션, 동적 속성 액세스 등의 메커니즘을 @ts-ignore사용할 수 있습니다.

형식 어설 션 ( as/ <>) 및 any형식화 된 변수 할당의 예 :

class A {
    constructor(private a: number) { }
}

const a = new A(10);
a.a; // TS compile error
(a as any).a; // works
const casted: any = a; casted.a // works

TS private이스케이프 해치 가있는 멤버 의 동적 속성 액세스를 허용 합니다 .

class C {
  private foo = 10;
}

const res = new C()["foo"]; // 10, res has type number

개인 액세스는 어디에서 의미가 있습니까? (1) 단위 테스트, (2) 디버깅 / 로깅 상황 또는 (3) 프로젝트 내부 클래스가있는 기타 고급 사례 시나리오 (오픈 엔드 목록).

내부 변수에 접근하는 것은 약간 모순적입니다. 그렇지 않으면 private처음 에는 만들지 않았을 것 입니다. 예를 들어, 단위 테스트는 개인 필드가 구현 세부 사항으로 숨겨져있는 검은 색 / 회색 상자 여야합니다. 그러나 실제로는 경우에 따라 유효한 접근 방식이있을 수 있습니다.

모든 ES 환경에서 사용 가능

TS private수정자는 모든 ES 대상과 함께 사용할 수 있습니다. #-비공개 필드는 target ES2015/ ES6이상 에서만 사용할 수 있습니다 . ES6 +에서는 WeakMap내부적으로 하위 구현으로 사용됩니다 ( 여기 참조 ). 기본 개인 #필드에는 현재 필요합니다 target esnext.

일관성과 호환성

팀은 코딩 지침과 린터 규칙을 사용 private하여 유일한 액세스 수정 자로 사용하도록 할 수 있습니다. 이 제한은 일관성을 유지하는 데 도움이되고 #이전 버전과 호환되는 방식으로 -private 필드 표기법 과 혼동되지 않습니다.

필요한 경우 매개 변수 속성 (생성자 할당 속기)이 쇼 스토퍼입니다. private키워드 에만 사용할 수 있으며 아직 개인 필드 에 구현할 계획없습니다# .

다른 이유들

  • private일부 하위 수준의 경우 더 나은 런타임 성능을 제공 할 수 있습니다 ( 여기 참조 ).
  • 현재까지 TS에는 사용 가능한 하드 프라이빗 클래스 메소드가 없습니다.
  • 어떤 사람들은 private키워드 표기법을 더 좋아합니다 😊.

둘 다 참고

두 가지 방법 모두 컴파일 타임에 일종의 명목 또는 브랜드 유형을 만듭니다.

class A1 { private a = 0; }
class A2 { private a = 42; }

const a: A1 = new A2();
// error: "separate declarations of a private property 'a'"
// same with hard private fields

또한 두 인스턴스 모두 인스턴스 간 액세스를 허용합니다. 클래스 인스턴스 A는 다른 A인스턴스의 개인 멤버에 액세스 할 수 있습니다 .

class A {
    private a = 0;
    method(arg: A) {
        console.log(arg.a); // works
    }
}

출처


답변