instanceof

100pearlcent·2021년 10월 28일
0

JavaScript

목록 보기
18/22
post-thumbnail

instanceof 연산자

obj instanceof Class

instanceof 연산자를 통해 객체가 특정 클래스에 속하는지 아닌지 확인할 수 있고 상속 관계도 확인해준다

class Rabbit {}
let rabbit = new Rabbit();

alert(rabbit instanceof Rabbit); // true

objClass에 속하거나 Class를 상속받는 클래스에 속하면 true가 반환된다


function Rabbit() {}

alert(new Rabbit() instanceof Rabbit); // true

instanceof는 생성자 함수에서도 사용 가능


let arr = [1, 2, 3];
alert(arr instanceof Array); // true
alert(arr instance Object); // true

Array같은 내장 클래스에도 사용 가능

👉arr은 클래스 Object에도 속한다는 점 (Array는 프로토타입 기반으로 Object를 상속받음)


instanceof연산자는 프로토타입 체인을 거슬러 올라가며 인스턴스 여부나 상속 여부를 확인한다

⚡ 정적 메서드 Symbol.hasIntance를 사용하면 직접 확인 로직을 설정할 수도 있다


동작 알고리즘

// canEat 프로퍼티가 있으면 animal이라고 판단할 수 있도록
// instanceOf의 로직을 직접 설정
class Animal {
  static [Symbol.hasInstance](obj) {
    if (obj.canEat) return true;
  }
}

let obj = { canEat: true };

alert(obj instanceof Animal); // true, Animal[Symbol.hasInstance](obj)가 호출됨
  1. 클래스에 정적 메서드 Symbol.hasIntance가 구현되어 있다면, obj instanceof Class문 실행 시 Class[Symbol.hasInstance](obj)가 호출되고 호출 결과는 boolean 타입이어야한다 > 이런 규칙을 기반으로 instanceof의 동작을 커스터마이징 할 수 있다

obj.__proto__ === Class.prototype?
obj.__proto__.__proto__ === Class.prototype?
obj.__proto__.__proto__.__proto__ === Class.prototype?
...
// 이 중 하나라도 true라면 true를 반환
// 그렇지 않고 체인의 끝에 도달하면 false를 반환
  1. 하지만 대부분의 클래스엔 Symbol.hasInstance가 구현되어있지 않으므로 이럴 땐 일반적인 로직이 사용된다 👉 obj instanceOf ClassClass.prototypeobj 프로토타입 체인 상의 프로토타입 중 하나와 일치하는지 확인한다
class Animal {}
class Rabbit extends Animal {}

let rabbit = new Rabbit();
alert(rabbit instanceof Animal); // true

// rabbit.__proto__ === Rabbit.prototype
// rabbit.__proto__.__proto__ === Animal.prototype (일치)

상속받은 클래스를 사용하는 경우엔 두 번째 단계에서 일치여부가 확인된다



Object.prototype.toString

타입 확인을 위해 쓰인다

명세서에 따르면, 객체에서 내장 toString을 추출하는 것이 가능하다
이렇게 추출한 메서드는 모든 값을 대상으로 실행할 수 있다
호출 결과는 값에 따라 달라진다

  • 숫자형 - [object Number]
  • 불린형 - [object Boolean]
  • null - [object Null]
  • undefined - [object Undefined]
  • 배열 - [object Array]
  • 그 외 - 커스터마이징 가능

// 편의를 위해 toString 메서드를 변수에 복사함
let objectToString = Object.prototype.toString;

// 아래 변수의 타입은?
let arr = [];

alert( objectToString.call(arr) ); // [object Array]

let s = Object.prototype.toString;

alert( s.call(123) ); // [object Number]
alert( s.call(null) ); // [object Null]
alert( s.call(alert) ); // [object Function]

call을 사용해 컨텍스트를 this = arr로 설정하고 함수 objectToString을 실행
toString 알고리즘은 내부적으로 this를 검사하고 상응하는 결과를 반환한다


Symbol.toStringTag

let user = {
  [Symbol.toStringTag]: "User"
};

alert( {}.toString.call(user) ); // [object User]

특수 객체 프로퍼티 Symbol.toStringTag를 사용하면 toString의 동작을 커스터마이징 할 수 있다


// 특정 호스트 환경의 객체와 클래스에 구현된 toStringTag
alert( window[Symbol.toStringTag]); // Window
alert( XMLHttpRequest.prototype[Symbol.toStringTag] ); // XMLHttpRequest

alert( {}.toString.call(window) ); // [object Window]
alert( {}.toString.call(new XMLHttpRequest()) ); // [object XMLHttpRequest]

대부분의 호스트 환경은 자체 객체에 이와 유사한 프로퍼티를 구현해 놓고 있다
호스트 환경 고유 객체의 Symbol.toStringTag 값은 [object ...]로 쌓여진 값과 동일하다

👉 'typeof' 연산자의 강력한 변형들 (toStringtoStringTag)은 원시 자료형뿐만 아니라 내장 객체에도 사용할 수 있으며 커스터마이징 할 수 있다

내장 객체의 타입 확인을 넘어서 타입을 문자열 형태로 받고 싶다면 instanceof 대신, {}.toString.call을 사용하면 된다


0개의 댓글