내가 할 수 없어야하는데 왜 TypeScript 개인 멤버에 액세스 할 수 있습니까?


108

TypeScript에서 private 멤버의 구현을보고 있는데 약간 혼란 스럽습니다. Intellisense는 개인 멤버에 액세스하는 것을 허용하지 않지만 순수 JavaScript에서는 모든 것이 있습니다. 이것은 TS가 개인 구성원을 올바르게 구현하지 않는다고 생각합니다. 이견있는 사람?

class Test{
  private member: any = "private member";
}
alert(new Test().member);

IntelliSense가 alert ()가있는 행에서 개인 구성원을 제공하지 않는 이유가 궁금하십니까?
esrange

7
아니. 나는 TS가 intellisense에 대한 설탕 일 뿐이고 실제로 컴파일되는 JavaScript에 대한 것이 아닌데 왜 프라이빗이 있는지 궁금합니다. typescriptlang.org/Playground 에서 실행 된이 코드 는 비공개 멤버 값을 경고합니다.
Sean Feldman 2012 년

언급했듯이 항목을 비공개로하려면 비공개 컨텍스트에서 변수로 항목을 선언해야합니다. 프로토 타입에 추가하는 것보다 비효율적 일 수 있기 때문에 typescript가 이것을 수행하지 않는다고 생각합니다. 또한 (개인 회원이 클래스의 정말 일부) 유형 정의와 놨어요
셰인

프로토 타입에 존재하는 실제 개인 변수를 원하면 약간의 오버 헤드가 필요하지만 GitHub : github.com/KthProg/ClassJS 에서이를 수행하는 ClassJS라는 라이브러리를 작성했습니다 .
KthProg 2017

답변:


97

유형 검사와 마찬가지로 멤버의 개인 정보는 컴파일러 내에서만 적용됩니다.

개인 속성은 일반 속성으로 구현되며 클래스 외부의 코드는 액세스 할 수 없습니다.

클래스 내에서 진정으로 비공개로 만들려면 클래스의 멤버가 될 수 없으며 객체를 생성하는 코드 내 함수 범위 내에 생성 된 지역 변수가됩니다. 즉, this키워드를 사용하여 클래스의 구성원처럼 액세스 할 수 없습니다 .


25
자바 스크립트 프로그래머가 객체 생성자에 지역 변수를 넣고이를 private 필드로 사용하는 것은 드문 일이 아닙니다. 나는 그들이 이와 같은 것을 지원하지 않은 것에 놀랐습니다.
Eric

2
@Eric : TypeScript는 메서드를 생성자 내부에 프로토 타입으로 추가하는 대신 메서드에 프로토 타입을 사용하므로 생성자의 로컬 변수는 메서드에서 도달 할 수 없습니다. 클래스의 함수 래퍼 안에 지역 변수를 만드는 것이 가능할 수도 있지만 아직 그 방법을 찾지 못했습니다. 그러나 그것은 여전히 ​​개인 멤버가 아닌 로컬 변수입니다.
Guffa

40
이것은 제가 피드백을 제공 한 것입니다. 나는 그것이 공개 모듈 패턴을 생성하는 옵션을 제공해야한다고 생각한다. 그래서 비공개 멤버는 비공개로 유지 될 수 있고 공개 멤버는 자바 스크립트에서 접근 할 수있다. 이것은 일반적인 패턴이며 TS와 JS에서 동일한 접근성을 제공합니다.
John Papa

당신이 개인 정적 멤버에 사용할 수있는 솔루션이있다 : basarat.com/2013/03/real-private-static-class-members-in.html
basarat은

1
@BasaratAli : 이것은 클래스의 메서드 내에서 사용할 수있는 정적 변수이지만 클래스의 구성원이 아닙니다. 즉 this키워드를 사용하여 액세스하지 않습니다 .
Guffa 2013 년

37

JavaScript는 개인 변수를 지원합니다.

function MyClass() {
    var myPrivateVar = 3;

    this.doSomething = function() {
        return myPrivateVar++;        
    }
}

TypeScript에서 이것은 다음과 같이 표현됩니다.

class MyClass {

    doSomething: () => number;

    constructor() {
        var myPrivateVar = 3;

        this.doSomething = function () {
            return myPrivateVar++;
        }
    }
}

편집하다

이 방법은에만 사용해야 아껴서 이 절대적으로 필요한 곳. 예를 들어 암호를 임시로 캐시해야하는 경우입니다.

이 패턴 (Javascript 또는 Typescript와 무관)을 사용하면 성능 비용이 발생하며 절대적으로 필요한 경우에만 사용해야합니다.


typescript는 var _this범위 함수에서 사용하도록 설정하여 항상 이것을 수행하지 않습니까? 왜 클래스 범위에서 퀄름을 사용합니까?
DrSammyD

아니요. var _this는 이것에 대한 참조 일뿐입니다.
Martin

2
더 정확하게는 private이 아닌 생성자 변수를 호출합니다. 프로토 타입 메소드에서는 볼 수 없습니다.
Roman M. Koss

1
오 예, 문제는 다른 문제였습니다. 생성하는 모든 인스턴스에 대해 doSomething이 다시 생성된다는 사실은 프로토 타입 체인의 일부가 아니기 때문입니다.
Barbu Barbu

1
@BarbuBarbu 네 동의합니다. 이는이 접근 방식의 큰 문제이며이를 피해야하는 이유 중 하나입니다.
Martin

11

WeakMap 에 대한 지원 이 더 널리 사용 가능 해지면 여기 예제 # 3에 자세히 설명 된 흥미로운 기술이 있습니다 .

개인 데이터를 허용하고 인스턴스 메서드 만이 아닌 프로토 타입 메서드에서 데이터에 액세스 할 수 있도록함으로써 Jason Evans 예제의 성능 비용을 피할 수 있습니다.

링크 된 MDN WeakMap 페이지에는 Chrome 36, Firefox 6.0, IE 11, Opera 23 및 Safari 7.1에서의 브라우저 지원이 나열됩니다.

let _counter = new WeakMap();
let _action = new WeakMap();
class Countdown {
  constructor(counter, action) {
    _counter.set(this, counter);
    _action.set(this, action);
  }
  decrement() {
    let counter = _counter.get(this);
    if (counter < 1) return;
    counter--;
    _counter.set(this, counter);
    if (counter === 0) {
      _action.get(this)();
    }
  }
}

나는 그것을 좋아! 기본적으로 개인 속성을 집계 된 클래스로 숨기는 것을 의미합니다. 가장 재미있는 것은 ... protected매개 변수 에 대한 지원을 추가하는 것은 어떻습니까? : D
Roman M. Koss

2
@RamtinSoltani 링크 된 문서는 weakmap이 작동하는 방식으로 인해 가비지 수집을 방지하지 않을 것이라고 통계합니다. 누군가가이 기술을 사용하는 동안 더욱 안전 해지기를 원한다면 각 weakmap에서 클래스 인스턴스 키를 삭제하는 자체 처리 코드를 구현할 수 있습니다.
Ryan Thomas

1
다음 MDN 페이지에서 developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/... . 대조적으로, 네이티브 WeakMap은 키 객체에 대한 "약한"참조를 보유합니다. 즉, 키 객체에 대한 다른 참조가 없을 경우 가비지 수집을 방지하지 않습니다. 이것은 또한 맵에서 값의 가비지 수집을 방지합니다.
Ryan Thomas

@RyanThomas 사실, 그것은 내가 얼마 전에 남긴 오래된 코멘트였습니다. 지도와 달리 WeakMaps는 메모리 누수를 일으키지 않습니다. 따라서이 기술을 사용하는 것이 안전합니다.
Ramtin Soltani

@RamtinSoltani 그래서 이전 댓글을 삭제 하시겠습니까?>
ErikE

10

TypeScript 3.8이 출시 될 예정이므로 포함하는 클래스 외부에서 액세스하거나 감지 할 수없는 비공개 필드를 선언 할 수 있습니다 .

class Person {
    #name: string

    constructor(name: string) {
        this.#name = name;
    }

    greet() {
        console.log(`Hello, my name is ${this.#name}!`);
    }
}

let jeremy = new Person("Jeremy Bearimy");

jeremy.#name
//     ~~~~~
// Property '#name' is not accessible outside class 'Person'
// because it has a private identifier.

개인 필드는 #문자로 시작 합니다.

이 비공개 필드는 private키워드 로 표시된 필드와 다른 것입니다.

Ref. https://devblogs.microsoft.com/typescript/announcing-typescript-3-8-beta/


4

이 문제에 대한 공식 토론 링크에 대한 Sean Feldman에게 감사드립니다. 링크에 대한 그의 답변 을 참조하십시오 .

그가 링크 한 토론을 읽었으며 여기에 주요 요점에 대한 요약이 있습니다.

  • 제안 : 생성자의 개인 속성
    • 문제 : 프로토 타입 함수에서 액세스 할 수 없음
  • 제안 : 생성자의 개인 메서드
    • 문제점 : 속성과 동일하며 프로토 타입에서 클래스 당 한 번 함수를 생성하는 성능 이점을 잃게됩니다. 대신 각 인스턴스에 대한 함수 사본을 만듭니다.
  • 제안 : 속성 액세스를 추상화하고 가시성을 강화하기 위해 상용구 추가
    • 문제 : 주요 성능 오버 헤드; TypeScript는 대규모 응용 프로그램을 위해 설계되었습니다.
  • 제안 : TypeScript는 이미 생성자 및 프로토 타입 메서드 정의를 클로저로 래핑합니다. 거기에 개인 메서드와 속성을 넣어
    • 그 클로저에 private 속성을 넣는 문제 : 그것들은 정적 변수가됩니다. 인스턴스 당 하나가 없습니다.
    • 해당 클로저에 개인 메서드를 넣는 데 문제 가 있습니다. this일종의 해결 방법 없이는 액세스 할 수 없습니다.
  • 제안 : 개인 변수 이름을 자동으로 변경
    • 반대 인수 : 이는 언어 구조가 아닌 명명 규칙입니다. 직접 엉망으로 만드십시오.
  • 제안 : 주석 @private이 메서드 이름을 효과적으로 축소 할 수 있음을 인식하는 축소자를 사용하여 비공개 메서드에 주석을 추가 합니다.
    • 이것에 대한 중요한 반론이 없습니다.

내 보낸 코드에서 가시성 지원 추가에 대한 전반적인 반론 :

  • 문제는 JavaScript 자체에 가시성 수정자가 없다는 것입니다. 이것은 TypeScript의 문제가 아닙니다.
  • JavaScript 커뮤니티에는 이미 확립 된 패턴이 있습니다. "자신의 책임하에 진행하십시오"라는 밑줄이있는 개인 속성 및 메서드 접두사
  • TypeScript 디자이너가 진정으로 사적인 속성과 메서드가 "가능하지 않다"고 말했을 때, 그들은 "우리의 디자인 제약 하에서 가능하지 않다"를 의미합니다. 특히 :
    • 내 보낸 JS는 관용적입니다.
    • 보일러 플레이트가 최소화 됨
    • 일반 JS OOP에 비해 추가 오버 헤드 없음

이 대화의 답변 인 경우 typescript.codeplex.com/discussions/397651- , 링크를 제공하세요. : D
Roman M. Koss

1
예, 그게 대화입니다.하지만 저는 이 질문에 대한 Sean Feldman의 답변에 연결했습니다. 여기서 그가 링크를 제공합니다. 그가 링크를 찾는 작업을했기 때문에 나는 그에게 공로를 인정하고 싶었습니다.
alexanderbird

2

TypeScript에서 Private 함수는 클래스 내에서만 액세스 할 수 있습니다. 처럼

여기에 이미지 설명 입력

그리고 비공개 회원에 접근을 시도하면 오류가 표시됩니다. 다음은 그 예입니다.

여기에 이미지 설명 입력

참고 : 자바 스크립트에서는 문제가 없으며 두 기능 모두 외부에서 액세스 할 수 있습니다.


4
OP : "하지만 순수 JavaScript에서는 모든 것이 있습니다."-생성 된 JavaScript가 "비공개"기능을 공개적으로 노출하는 문제를 해결하지 않은 것 같습니다
alexanderbird

1
@alexanderbird 나는 그가 TypeScript로 보통 충분하다고 말하고 싶었다고 생각합니다. TypeScript로 개발할 때 프로젝트 범위에서 그대로 유지하므로 JavaScript의 개인 정보 보호는 큰 문제가 아닙니다. 무엇보다도 원본 코드는 트랜스 파일 (JavaScript) 코드가 아니라 개발자에게 중요합니다.
Roman M. Koss

1
있는 거 쓰기와 자바 스크립트 라이브러리를 게시하면 않는 한, 다음 transpiled 코드는 중요합니까
alexanderbird

당신의 대답은 주제를 벗어났습니다.
canbax

1

나는 이것이 오래된 논의라는 것을 알고 있지만 컴파일 된 JavaScript 클래스의 공용 인터페이스로 "누출"되는 TypeScript의 개인 변수 및 메서드 문제에 대한 내 솔루션을 공유하는 것이 여전히 유용 할 수 있습니다.

나에게이 문제는 순전히 외형적인 문제입니다. 즉, DevTools에서 인스턴스 변수를 볼 때 시각적 인 혼란에 관한 것입니다. 내 수정 사항은 다른 클래스 내에서 개인 선언을 그룹화 한 다음 기본 클래스에서 인스턴스화하고 (이중 밑줄) private과 같은 이름을 가진 (JS에서는 여전히 공개적으로 볼 수 있음) 변수에 할당하는 것 __입니다.

예:

class Privates {
    readonly DEFAULT_MULTIPLIER = 2;
    foo: number;
    bar: number;

    someMethod = (multiplier: number = this.DEFAULT_MULTIPLIER) => {
        return multiplier * (this.foo + this.bar);
    }

    private _class: MyClass;

    constructor(_class: MyClass) {
        this._class = _class;
    }
}

export class MyClass {
    private __: Privates = new Privates(this);

    constructor(foo: number, bar: number, baz: number) {
        // assign private property values...
        this.__.foo = foo;
        this.__.bar = bar;

        // assign public property values...
        this.baz = baz;
    }

    baz: number;

    print = () => {
        console.log(`foo=${this.__.foo}, bar=${this.__.bar}`);
        console.log(`someMethod returns ${this.__.someMethod()}`);
    }
}

let myClass = new MyClass(1, 2, 3);

myClass인스턴스를 DevTools에서 볼 때 모든 "비공개"멤버가 진정한 공개 멤버와 혼합 된 것을 보는 대신 (적절하게 리팩토링 된 실제 코드에서 시각적으로 매우 지저분해질 수 있음) 축소 된 __속성 내에서 깔끔하게 그룹화되는 것을 볼 수 있습니다.

여기에 이미지 설명 입력


1
나는 그것을 좋아한다. 깨끗해 보입니다.

0

적절한 개인 속성을 추가하기위한 재사용 가능한 접근 방식은 다음과 같습니다.

/**
 * Implements proper private properties.
 */
export class Private<K extends object, V> {

    private propMap = new WeakMap<K, V>();

    get(obj: K): V {
        return this.propMap.get(obj)!;
    }

    set(obj: K, val: V) {
        this.propMap.set(obj, val);
    }
}

Client두 개의 개인 속성이 필요한 어딘가에 클래스가 있다고 가정 해 보겠습니다 .

  • prop1: string
  • prop2: number

다음은이를 구현하는 방법입니다.

// our private properties:
interface ClientPrivate {
    prop1: string;
    prop2: number;
}

// private properties for all Client instances:
const pp = new Private<Client, ClientPrivate>();

class Client {
    constructor() {
        pp.set(this, {
            prop1: 'hello',
            prop2: 123
        });
    }

    someMethod() {
        const privateProps = pp.get(this);

        const prop1 = privateProps.prop1;
        const prop2 = privateProps.prop2;
    }
}

그리고 필요한 모든 것이 하나의 사유 재산이라면 ClientPrivate, 그 경우에는 어떤 것도 정의 할 필요가 없기 때문에 훨씬 더 간단 해집니다 .

대부분의 경우 클래스 Private는 읽기 쉬운 서명을 제공하지만 직접 사용은 제공 WeakMap하지 않습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.