TypeScript - this, modifier

이소라·2022년 6월 4일
0

TypeScript

목록 보기
3/28

this

this behavior in class

  • this는 함수가 어떻게 실행되는냐에 의해 결정됨
class MyClass {
  name = "MyClass";
  getName() {
    return this.name;
  }
}
const c = new MyClass();
const obj = {
  name: "obj",
  getName: c.getName,
};
 
// Prints "obj", not "MyClass"
console.log(obj.getName());
  • obj에서 getName 메소드를 호출했으므로, this는 MyClass가 아닌 obj를 참조함

this parameter

  • 메소드의 매개변수로 클래스를 참조하는 this를 넣으면, 해당 클래스와 같은 형태의 객체(인스턴스)에서 메소드를 호출할 경우에만 에러나지 않고 실행됨
- 

class MyClass {
  name = "MyClass";
  getName(this: MyClass) {
    return this.name;
  }
}
const c = new MyClass();
// OK
c.getName();
 
// Error, would crash
const g = c.getName;
console.log(g());
  • 인스턴스에서 메소드를 호출할 경우, 에러가 발생하지 않음
  • 메소드를 변수로 저장하여 호출한 경우, 에러가 발생함
class Department {
  name: string;

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

  describe(this: Department) {
    console.log('Department: ' + this.name);
  }
}

const accounting = new Department('Accounting');

const accountingCopy = { name: 'DUMMY', describe: accounting.describe };
//OK
accountingCopy.describe();
  • 클래스와 같은 속성을 가진 객체에서 메소드를 호출할 경우, 에러가 발생하지 않음

this Type

  • 클래스에서 this 타입은 현재 클래스의 타입으로 동적으로 참조됨
class Box {
  contents: string = "";
  set(value: string) {
    this.contents = value;
    return this;
  }
}

class ClearableBox extends Box {
  clear() {
    this.contents = "";
  }
}
 
const a = new ClearableBox();
const b = a.set("hello");
  • b의 this는 ClearableBox를 참조함
  • 매개변수의 타입으로 this를 할당할 수 있음
class Box {
  content: string = '';
  sameAs(other: this) {
    return other.content === this.content;
  }
}

class ClearableBox extends Box {
  otherContent: string = '?';
}

const box = new Box();
const clearBox = new ClearableBox();
// Error
console.log(clearBox.sameAs(box));
  • ClearbleBox 클래스의 인스턴스에서 this는 ClearableBox를 참조하므로, sameAs 메소드는 매개변수로 ClearbaleBox의 인스턴스만 받을 수 있음

참고링크 : TypeScript Handook - this

Modifier

public

  • 기본 visibility modifier
    • class member에 명시하지 않아도 기본적으로 설정됨
  • public으로 선언된 member는 어디서든 접근 가능함
class Department {
  name: string;
  constructor(n: string) {
    this.name = n;
  }

  describe(this: Department) {
    console.log('Department: ' + this.name);
  }
}

const accounting = new Department('Accounting');
//OK
accounting.name = 'Max';
  • name이 public이므로, 외부에서 접근 가능함
class Greeter {
  public greet() {
    console.log("hi!");
  }
}
const g = new Greeter();
g.greet();
  • greeting 메소드가 public이므로, 외부에서 호출 가능함

protected

  • protected로 선언한 member는 선언한 클래스와 그 하위 클래스에서만 접근 가능함
class Greeter {
  public greet() {
    console.log("Hello, " + this.getName());
  }
  protected getName() {
    return "hi";
  }
}
 
class SpecialGreeter extends Greeter {
  public howdy() {
    // OK to access protected member here
    console.log("Howdy, " + this.getName());
  }
}
const g = new SpecialGreeter();
g.greet(); // 'Hello, hi'
g.howdy(); // 'Howdy, hi'
g.getName(); //Error
  • Greeter 클래스에서 protected로 선언된 getName 메소드은 하위 클래스인 SpecialGreeter의 howdy 메소드 내에서 접근 가능하지만, 외부에서 접근할 수 었음
class Base {
  protected x: number = 1;
}
class Derived1 extends Base {
  protected x: number = 5;
}
class Derived2 extends Base {
  f1(other: Derived2) {
    other.x = 10;
  }
  f2(other: Base) {
    other.x = 10; //Error
  • Derived1에서 protected로 선언한 x는 Derived1의 하위 클래스에서만 접근 가능함

private

  • private으로 선언한 member는 선언한 클래스에서만 접근 가능함
class Department {
  name: string;
  private employees: string[] = [];
  constructor(n: string) {
    this.name = n;
  }

  describe(this: Department) {
    console.log('Department: ' + this.name);
  }

  addEmployee(employee: string) {
    this.employees.push(employee);
  }

  printEmployeeInfo() {
    console.log(this.employees.length);
    console.log(this.employees);
  }
}

const accounting = new Department('Accounting');

accounting.addEmployee('Max');
accounting.addEmployee('Maru');
accounting.printEmployeeInfo(); // 2
accounting.employees[2] = 'Marry' // Error
  • private으로 선언한 employees는 선언한 클래스의 메소드를 통해서만 조작 가능함
  • 외부에서 접근하면 에러가 발생함
class Base {
  private x = 0;
}
const b = new Base();
// Can't access from outside the class
console.log(b.x);

class Derived extends Base {
  showX() {
    // Can't access in subclasses
    console.log(this.x);
Property 'x' is private and only accessible within class 'Base'.
  }
}
  • private으로 선언한 memeber는 하위 클래스에서도 접근 불가능함
class A {
  private x = 10;
 
  public sameAs(other: A) {
    // No error
    return other.x === this.x;
  }
}
  • TypeScript는 같은 클래스의 다른 인스턴스에서 서로의 private으로 선언된 member에 접근 가능함
  • private로 선언된 member는 타입을 확인할 때 []를 사용하여 접근 가능함
class MySafe {
  private secretKey = 12345;
}
 
const s = new MySafe();
 
// Not allowed during type checking
console.log(s.secretKey);
 
// OK
console.log(s["secretKey"]);

readonly

  • readonly를 선언한 member는 재할당이 불가능함
class Department {
  constructor(private readonly id: string, public name: string) {}

  describe(this: Department) {
    // Error
    this.id = d2;
    console.log(`Department ${this.id}: ` + this.name);
  }
}

const accounting = new Department('id1', 'Max');
  • id는 readonly 속성이므로 재할당이 불가능함

TypeScript와 JavaScript의 privacy

  • privateprotected는 타입을 확인할 때 실행됨
    • JavaScript runtime에서는 private이나 protected로 선언된 member에 접근 가능함
class MySafe {
  private secretKey = 12345;
}
Try
// In a JavaScript file...
const s = new MySafe();
// Will print 12345
console.log(s.secretKey);
  • JavaScript의 private field(#)는 컴파일 이후에도 private으로 남아 있음
class Dog {
  #barkAmount = 0;
  personality = "happy";
 
  constructor() {}
}
  • TypeScript는 #의 자리에 WeakMap을 사용함
    • WeakMap
      • key를 약하게 참조하는 key-value Collection
      • key는 Object 타입만 가능함
      • 가비지 컬렉션을 방지하지 않으므로, 가비지 컬렉션 시 key 참조가 사라짐
      • WeakMap의 key는 열거할 수 없음
      • key의 목록이 필요하면 Map을 사용해야함
        참고링크 : MDN, TOAST UI
"use strict";
var _Dog_barkAmount;
class Dog {
    constructor() {
        _Dog_barkAmount.set(this, 0);
        this.personality = "happy";
    }
}
_Dog_barkAmount = new WeakMap();
  • 강한 runtime privacy를 주려면 closure, WeakMap, private field(#) 를 사용해야함
  • runtime 동안 privacy 확인이 추가되면 성능에 영향을 줄 수 있음

참고링크 : TypeScript Handook - Member visibility

0개의 댓글