[Javascript] x !== x 는 true가 될 수 있나?

x!==x

코딩 테스트에 자주 나오는 잘 알려진 문제입니다.

유사한 문제들이 몇 가지 있습니다.

x == 1 && x == 2 && x == 3 은 true가 될 수 있나?

x === x+1 은 true가 될 수 있나?

같은 개념을 물어보는 질문이고, 답부터 말하면 객체에 접근할 때마다 게터(getter)로 반환 값을 누적하거나 변경해서 달라지게 하는  방법을 알고 있는지를 묻는 질문입니다.

문제를 푸는 가장 기초적인 접근은 x가 변수가 아니라 객체, 또는 객체의 속성이라는 것에서부터 출발합니다.

객체, 또는 객체 속성이구나! 가 떠오르지 않으면 영원히 문제를 풀 수 없습니다.

예외적으로 x !== x는 조금 다른 해결 방법도 있습니다.

다음 조건절의 결과가 "다름"이 표시되어야 합니다.

if(x !== x){console.log('다름');

다음 속성을 전역으로 생성합니다.

전역 속성(window.x)에 접근할 때마다 게터가 랜덤 값을 생성해서 반환하므로 비교 연산자로 x를 비교하면 2개의 랜덤값을 비교하게 됩니다.

Object.defineProperty(window, 'x', {
    get () {
        return Math.random()
    }
})

if(window.x !== window.x){
    console.log('다름')
}

더 간단한 방법이 있습니다.

NaN(Not a Number)은 특수한 데이터 타입으로 예외적으로 isNaN() 메서드로 NaN 인지 확인을 할 수 있습니다. 그 외의 불리언 결과 값을 확인하는 모든 비교는 false를 반환합니다. NaN과 NaN을 비교해도 false가 됩니다.

let x = NaN
if(x !== x){
    console.log('다름')
}

x===1 && x===2 && x===3

전역 변수를 하나 생성해서 전역 속성을 접근할 때마다 전역 변수 값을 더해서 값이 누적되도록 합니다.

y=0
Object.defineProperty(window, 'x', {
    get () {
        y++
        return y
    }
})

if(x===1 && x===2 && x===3){
    console.log('같음')
}

x > x

Symbol.toPrimitive 는 Symbol 객체의 정적 데이터 속성으로 객체의 프리미티브 데이터를 반환하는 속성입니다.

객체에 프로토타입으로 이 속성을 재정의해서 객체 안의 변수 값이 누적 더하기가 되도록 하면 객체를 접근할 때마다 객체 안의 변수 val 값이 1씩 더해진 숫자(Number)를 반환합니다.

const x = {
  val: 0,
  [Symbol.toPrimitive](){return this.val++}
}

if (x < x) {
  console.log('크다')
}

Symbol.toPrimitive 속성

상식적으로 보면 비교할 수 없는 조건문들을 Symbol.toPrimitive 속성을 이용해 가능하게 할 수 있습니다.

전역 속성을 사용 하는 방법이 다소 원시적인 접근 방법이라면 Symbol.toPrimitive 속성 접근 방법은 객체의 속성을 이용해서 원하는 값을 반환할 수 있기 때문에 구현 결과가 좀 더 세련되어 보입니다.

Symbol.toPrimitive 속성으로 프리미티브 숫자를 반환하는 속성이 실행되려면 연산자, 또는 대소를 비교하는 비교 연산자를 객체에 사용해야 합니다.

같은지를 비교하는 연산자를 사용할 때는 한 가지 주의할 점이 있습니다. 값을 비교하는 "=="는 Symbol.toPrimitive가 반환하는 값과 비교할 수 있어서 같은지 비교를 할 수 있지만, 타입까지 비교하는 "==="는 항상 false가 됩니다. Symbol.toPrimitive로 숫자를 반환하지만, 어디까지나 타입은 객체(Object)입니다.

const x = {
  value: 0,
  [Symbol.toPrimitive](){return ++this.value;}
}

if (x==1) {
  console.log('값이 같다')
}
if (x===2) {
  console.log('타입도 같다')
}
console.log(typeof x)

Symbol.toPrimitive 속성을 사용하면 다음과 같은 비교도 가능합니다.

const x = {
  value: 1,
  [Symbol.toPrimitive](){this.value=this.value*-1;return this.value;}
}

if (x<0 && x>0) {
  console.log('0아님')
}