[JavaScript] instanceof 연산자

기본 사용법

class Person { }
class Student extends Person { }

const student = new Student();

console.log(student instanceof Student); // true: student의 프로토타입 체인에 Student.prototype이 있음
console.log(student instanceof Person);  // true: Student.prototype의 [[Prototype]]이 Person.prototype을 가리킴

위 예제에서 student 객체는 Student와 Person의 인스턴스로 모두 인식됩니다. 이는 instanceof 연산자가 객체의 프로토타입 체인을 따라 부모 클래스까지 확인하기 때문입니다.

Symbol.hasInstance를 통한 동작 커스터마이징

클래스에 Symbol.hasInstance 메서드를 정의하면 instanceof 연산자의 동작을 원하는 대로 변경할 수 있습니다. 이는 obj instanceof Class를 평가할 때 내부적으로 Class[Symbol.hasInstance](obj)가 호출되기 때문입니다.

class EvenNumber {
  // 객체의 value 프로퍼티가 짝수이면 true를 반환
  static [Symbol.hasInstance](obj) {
    return typeof obj.value === 'number' && obj.value % 2 === 0;
  }
}

const num1 = { value: 4 };
const num2 = { value: 7 };

console.log(num1 instanceof EvenNumber); // true
console.log(num2 instanceof EvenNumber); // false

위 예제에서 EvenNumber 클래스는 Symbol.hasInstance를 통해 객체의 value 프로퍼티가 짝수인지 검사하여 instanceof의 결과를 결정합니다.

프로토타입 체인과 isPrototypeOf

instanceof 연산자는 내부적으로 대상 객체의 프로토타입 체인에 특정 생성자의 prototype이 존재하는지를 확인합니다. 이 동작은 Object.prototype.isPrototypeOf 메서드를 사용하여 동일하게 구현할 수 있습니다.

console.log(Student.prototype.isPrototypeOf(student)); // true
console.log(Person.prototype.isPrototypeOf(student));  // true

student instanceof Student는 Student.prototype.isPrototypeOf(student)와 동일한 결과를 반환합니다. 이는 객체의 프로토타입 체인을 따라 prototype이 존재하는지를 확인하는 방식입니다.

Object.prototype.toString을 활용한 정확한 타입 확인

typeof 연산자는 원시 타입과 함수 여부를 확인하는 데 유용하지만, 객체의 구체적인 타입(예: Array, Date 등)을 확인하기에는 제한적입니다. 보다 정확한 타입 정보를 얻기 위해 Object.prototype.toString 메서드를 사용할 수 있습니다.

const toString = Object.prototype.toString;

console.log(toString.call(new Date()));      // [object Date]
console.log(toString.call([1, 2, 3]));       // [object Array]
console.log(toString.call({}));              // [object Object]
console.log(toString.call(/regex/));         // [object RegExp]
console.log(toString.call(null));            // [object Null]
console.log(toString.call(undefined));       // [object Undefined]

Symbol.toStringTag를 사용한 커스터마이징

내장 객체들은 Symbol.toStringTag 프로퍼티를 사용하여 Object.prototype.toString의 반환 값을 커스터마이징할 수 있습니다. 사용자 정의 클래스에서도 이 프로퍼티를 정의하여 원하는 태그를 지정할 수 있습니다.

class CustomError extends Error {
  constructor(message) {
    super(message);
  }

  get [Symbol.toStringTag]() {
    return 'CustomError';
  }
}

const error = new CustomError('문제가 발생했습니다.');
console.log(Object.prototype.toString.call(error)); // [object CustomError]

위 예제에서 CustomError 클래스는 Symbol.toStringTag를 통해 타입 태그를 "CustomError"로 설정하였습니다. 이를 통해 Object.prototype.toString 호출 시 반환 값이 [object CustomError]가 됩니다.