자바스크립트는 프라이빗한 속성을 줄 수 있는 속성이 없었다
그래서 컨벤션을 이용한 방법으로 _ 언더스코어 프리픽스를 속성 명에 관용적으로 많이 사용했다
class SomeConstructor {
constructor() {
this._privateProp = 'dont touch this';
this.publicProp = 'you can touch this';
}
}
이 방법은 컨벤션을 이용해 private로 취급할 뿐이지 당연히 실제로는 public으로 동작하기 때문에 외부에서 얼마든지 접근 할 수 있다
근본적으로 접근이 불가능한 private 속성을 만드는 방법으로는 클로저를 이용하는 방법이 있다
class SomeConstructor {
constructor() {
const privateProp = 'dont touch this';
this.publicProp = 'you can touch this';
this.doSomethingWithPrivateProp = () => { };
}
}
this
를 사용해 데이터에 접근하는 문법과 모양새가 달라지기 때문에 this
컨텍스트와 혼용할 때는 코드의 일관성이 잃어 가독성이 떨어질 수 있지만 효과적으로 데이터를 격리할 수 있었다
인스턴스 컨텍스트와 차원이 분리될 정도의 격리다
이러한 접근 방법은 데이터를 숨기는 것에도 유용하지만 메서드를 숨기는데도 유용하다
모듈패턴
function SomeModule() {
const privateProp = 'dont touch this';
const publicProp = 'you can touch this';
_doSomethingWithPrivateProp = () => { ... }
const publicMethod = () => {
_doSomethingWithPrivateProp();
}
return {
publicProp,
publicMethod
}
}
모듈패턴은 ES6의 등장으로 점차 사라져갔다
Symbol
을 사용해 private 속성을 만들 수 있다
const privateMethodName = Symbol();
const privatePropName = Symbol();
class SomeClass {
[privatePropName] = 'dont touch this';;
publicProp = 'you can touch this';
[privateMethodName]() {
console.log('private method');
}
publicMethod() {
this[privateMethodName](this[privatePropName]);
}
}
모듈 스코프 안에서 symbol을 사용할 수 있어 해당 필드나 메서드에 접근할 수 있지만 symbol을 export하지 않는 한 외부에서 접근할 수 없다
접근할 필드의 이름을 무엇인지 모르기 때문이다
자바스크립트에서도 언어가 제공하는 정상적인 방법으로 클래스에 private 속성을 만들 수 있게 되었다
private
와 같은 키워드를 사용하는 것이 아니라 #, 샵 프리픽스를 사용한다
속성 명 앞에 #이 붙으면 Private 필드로 동작한다
class Student {
#age = 10;
}
const student = new Student();
console.log(lee.#age); //Error TS18013: Property '#age' is not accessible outside class 'Student' because it has a private
#age
라는 속성에 접근하려고 하면 에러 메시지가 출력된다
class Student {
#age = 10;
getAge() {
return this.#age;
}
}
const student = new Student();
console.log(student.getAge()); // 10
외부에 getAge()
라는 getter를 노출해 #age
값에 접근할 수 있다
private로 만든 속성은 해당 private 속성이 정의된 클래스를 제외하고 접근이 불가능하다
상속 받은 클래스도 마찬가지이다
하지만 독특한 특징이 있다
private 속성의 독특함이 아니라 자바스크립트이기 때문에 유달리 독특해 보이는 특징이 있다
이 특징은 모든 Private 필드는 소속된 클래스에 고유한 스코프를 갖는다라는 내용에 의해 발생한다
class Student {
age = 10;
getAge() {
return this.age;
}
}
class Lee extends Student {
age = 20;
getFakeAge() {
return this.age;
}
}
const lee = new Lee();
console.log(lee.ageAge()); // 20
console.log(lee.getFakeAge()); // 20
위 예제는 private를 사용하지 않았다
Student
를 상속한 Lee 객체에서 age를 중복으로 선언하고 다른 이름의 게터 getFakeAge()
를 정의했다
public 속성이라면 this 컨텍스트에는 age
속성이 하나이기 때문에 age의 값이 20이다
이는 Student
의 getAge()
룰 실행하던 Lee
의 getFakeAge()
를 실행하던 동일하게 20이다
왜냐면 애초에 this가 가리키는 인스턴스 컨텍스트에 age는 하나밖에 없기 때문이다
age
에 private 속성을 #age
로 바꾼다면
class Student {
#age = 10;
getAge() {
return this.#age;
}
}
class Lee extends Student {
#age = 20;
getFakeAge() {
return this.#age;
}
}
const lee = new Lee();
console.log(lee.ageAge()); // 10
console.log(lee.getFakeAge()); // 20
#age
, private 속성은 그동안 우리가 알고 있던 this의 그 컨텍스트와는 다른 방식으로 저장된다
기존처럼 인스턴스별로 독립적인 공간을 갖지만, 주가로 클래스별로 독립적인 공간을 갖는 것이다
쉽게 말하면 Student
클래스 스코프의 #age
와 Lee
클래스 스코프의 #age
는 다른 것이다
그러므로 Student
클래스에 속한 getAge()
가 실행될때는 Student
의 #age
접근하고
Lee
의 getFakeAge()
가 실행될 때는 Lee
의 #age
에 접근하는 것이다
이것이 바로 모든 Private 필드는 소속된 클래스에 고유한 스코프를 갖는다는 것이다