본문 바로가기
Javascript

JavaScript에서 Priavate 구현

by F.E.D 2020. 3. 15.
In its current state, there is no "direct” way to create a private variable in JavaScript. 
-> 현재 상태에서 자바스크립트 private 변수를 (은닉화) 만드는 "직접적인" 방법은 없습니다.
In other languages, you can use the private keyword or double-underscores and everything works,
-> 다른 언어에서, private 키워드(자바 등) 또는 __ 이중 underscore 를 사용하는 등의 방법을 사용할 수 있지만
but variable privacy in JavaScript carries characteristics that make it seem more akin to an emergent trait of the language rather than an intended functionality. 
-> JavaScript는 의도 된 기능보다는 언어의 특성과 더 유사하게 보이는 특성을 가지고 있습니다.

* 더욱이 함수 스코프를 가지고 있는 "var"를 사용할 때는 이런 방법이 거의 불가능하다고 봤어도 된다고 봅니다.

// 글로벌 변수 a 선언
var a = 123;

// 함수 스코프에 b선언
(function() {
  console.log(b); // 호이스팅 때문에 undefined 뜹니다.
  var b = 456;
})();

console.log(a); // => 123
console.log(b); // 참조 예외 발생 (에러) 
// b는 함수형 스코프 밖에서 접근할 수 없기 때문입니다.

* es6의 탄생으로 인해서 블록 범위 스코프인 let과 const가 나오게 됩니다.

const a = 123;

// 블록스코프 예시1
if (true) {
  const b = 345;
}

// 블록스코프 예시2
{
  const c = 678;
}

console.log(a); // 123
console.log(b); // 참조에러 블록스코프 밖에서 내부 변수 참조 불가
console.log(c); // 참조에러 블록스코프 밖에서 내부 변수 참조 불가

* 범위를 벗어난 코드는 변수에 액세스 할 수 없기 때문에 프라이버시의 특성을 얻습니다.

* 여러 가지 방법으로 구현하기위한 몇 가지 기술을 다룰 것입니다.

* JavaScript의 함수도 블록이므로 모든 가변 키워드가 작동합니다.

* 또한 "모듈"이라는 매우 유용한 디자인 패턴을 구현할 수 있습니다.

* 모듈 디자인 패턴은 공용 및 개인 구성 요소를 결합하고 프로그램을 더 작은 구성 요소로 나누고 "캡슐화"라는 프로세스를 통해 프로그램의 다른 부분에 액세스 할 수있는 부분 만 노출시키기 때문에 JavaScript에서 매우 유용합니다.

* 이 방법을 통해 우리는 사용해야 할 사항 만 공개하고 볼 필요가없는 나머지 구현을 숨길 수 있습니다.

* 이를 구현하기 위해 함수 범위를 활용할 수 있습니다.

 

const CarModule = () => {
  let milesDriven = 0;
  let speed = 0;

  const accelerate = (amount) => {
    speed += amount;
    milesDriven += speed;
  }

  const getMilesDriven = () => milesDriven;

  // "return" 키워드를 사용하여 무엇을 노출 시킬 지 숨길 지 결정할 수 있습니다.
  // 이 상황에서는 우리는 유일하게 accelerate() 함수와 getMilesDriven() 함수만을 노출시켜야합니다.
  return {
    accelerate,
    getMilesDriven
  }
};

const testCarModule = CarModule();
testCarModule.accelerate(5);
testCarModule.accelerate(4);
console.log(testCarModule.getMilesDriven());

이 상황에서 private 변수에 대한 이점이 분명해지기 시작합니다.

변수, 함수 또는 기타 내부 구성 요소에 액세스하는 기능을 제거하면 의도하지 않은 것을 실수로 다른 사람이 실수로 사용하여 발생하는 오류의 표면적을 줄일 수 있습니다.

 

 

function CarModule() {
  let milesDriven = 0;
  let speed = 0;

  // CarModule을 참조하지않고 this 키워드를 사용합니다.
  this.accelerate = (amount) => {
    speed += amount;
    milesDriven += speed;
  }

  this.getMilesDriven = () => milesDriven;
}

const testCarModule = new CarModule();
testCarModule.accelerate(5);
testCarModule.accelerate(4);
console.log(testCarModule.getMilesDriven());

* es6 CLASS를 사용한 방법

class CarModule {
  /*
    milesDriven = 0;
    speed = 0;
  */
  constructor() {
    this.milesDriven = 0;
    this.speed = 0;
  }
  accelerate(amount) {
    this.speed += amount;
    this.milesDriven += this.speed;
  }
  getMilesDriven() {
    return this.milesDriven;
  }
}

const testCarModule = new CarModule();
testCarModule.accelerate(5);
testCarModule.accelerate(4);
console.log(testCarModule.getMilesDriven());

 

* 언더스코어를 사용하는 방법도 있습니다.

* 밑줄 (_)로 변수 앞에 접두사를 붙이면 여전히 외부에 "표시"되지만 개발자에게 "이 변수를 만지지 마십시오." 라고 해줍니다.

/*
  _milesDriven = 0;
  _speed = 0;
*/
constructor() {
  this._milesDriven = 0;
  this._speed = 0;
}

 

* 기술적으로, 클래스에서 변수 프라이버시를위한 메소드가 있습니다. 

class CarModule {
  constructor() {
    let milesDriven = 0;
    let speed = 0;

    this.accelerate = (amount) => {
      speed += amount;
      milesDriven += speed;
    }

    this.getMilesDriven = () => milesDriven;
  }
}

const testCarModule = new CarModule();
testCarModule.accelerate(5);
testCarModule.accelerate(4);
console.log(testCarModule.getMilesDriven());
console.log(testCarModule.speed); // undefined

* weakMap을 사용한 방법도 있습니다.

class CarModule {
  constructor() {
    this.data = new WeakMap();
    this.data.set(this, {
      milesDriven: 0,
      speed: 0
    });
  }

  accelerate(amount) {
    // this keyword 대신에 weakMap을 사용합니다.
    const data = this.data.get(this);
    const speed = data.speed + amount;
    const milesDriven = data.milesDriven + data.speed;
    this.data.set({ speed, milesDriven });
  }

  this.getMilesDriven = () => this.data.get(this).milesDriven;
}

const testCarModule = new CarModule();
testCarModule.accelerate(5);
testCarModule.accelerate(4);
console.log(testCarModule.getMilesDriven());
console.log(testCarModule.data); //=> WeakMap { [items unknown] }

* 하지만 이 방법은 깊은 복사를 통해 새로운 CarModule을 선언하고 복사할 수 있으므로 실제로는 private이 아닙니다.

* 이것은 또 심볼을 사용하여 해결할 수 있습니다.

class CarModule {
  constructor() {
    this.speedKey = Symbol("speedKey");
    this.milesDrivenKey = Symbol("milesDrivenKey");
    this[this.speedKey] = 0;
    this[this.milesDrivenKey] = 0;
  }

  accelerate(amount) {
    this[this.speedKey] += amount;
    this[this.milesDrivenKey] += this[this.speedKey];
  }

  getMilesDriven() {
    return this[this.milesDrivenKey];
  }
}

const testCarModule = new CarModule();
testCarModule.accelerate(5);
testCarModule.accelerate(4);
console.log(testCarModule.getMilesDriven());
console.log(testCarModule.speed); // => undefined 

TC39 private class field proposal 라는 새로운 방법이 나왔습니다( 현재는 babel 사용하여 적용해야 함 )

단순하게 # 만 붙이면 됩니다.

 

class CarModule {
  #speed = 0
  #milesDriven = 0
  
  accelerate(amount) {
    this.#speed += amount;
    this.#milesDriven += speed;
  }

  getMilesDriven() {
    return this.#milesDriven;
  }
}

const testCarModule = new CarModule();
testCarModule.accelerate(5);
testCarModule.accelerate(4);
console.log(testCarModule.getMilesDriven());
console.log(testCarModule.speed); //=> undefined

 

 

 

 

출처 : https://css-tricks.com/implementing-private-variables-in-javascript/

댓글