%EB%B8%94%EB%A1%9C%EA%B7%B8-%EC%8D%B8%EB%84%A4%EC%9D%BC-035


멘토링 과정 시, ==와 ===의 차이점에 대한 질문이 나왔다. 단순하게 타입 차이(?)라고만 알고 있었던 터라 자신있게 대답하지 못했다.

이 차이점에 대해 한번 꼭 공부하고 넘어가야지 하던 중, 인사이드 자바스크립트 3장을 읽다가 동등 연산자(==)와 일치 연산자(===)의 차이에 대해 알게 되었다.

ECMAScript 명세서를 참고해 정확하게 짚고 넘어가자.




==와 ===

먼저 ==동등 연산자(coercive equality)이다. 비교 시 피연산자의 타입이 다를 경우 강제 변환을 허용한다. 따라서 비교하려는 피연산자의 타입이 다를 경우, 우선 타입 변환을 거친 후 비교한다.

===엄격 동등 연산자(strict equality)로써, 비교 시 피연산자의 타입이 다를 경우 강제 변환을 허용하지 않는다. 즉 비교하려는 피연산자의 타입이 달라도 타입을 변경하지 않고 비교한다.

console.log(1 == "1"); // true
console.log(1 === "1"); // false

동등 연산자의 경우, 두 피연산자의 타입이 다르므로 같은 타입으로 (숫자) 변환해서 두 값이 같다고 판단했다. 엄격 동등 연산자의 경우, 타입 변환을 거치지 않아 두 값이 같지 않다고 판단한다.

따라서 동등 연산자를 사용했을 경우에 타입 변환에 대한 잘못된 결과를 얻을 수 있으므로 대부분의 자바스크립트 코딩 가이드에서는 == 연산자로 비교하는 것을 추천하지 않는다.



1. IsLooselyEqual

== 연산자에서 비교를 위해 어떤 알고리즘을 따를까

ECMAScript 명세서를 참고해보면 해당 알고리즘을 따른다.

Untitled

  • x와 y의 타입이 동일하면 === 연산자 비교 알고리즘 (isStrictlyEqual)을 사용한다.
  • x와 y가 각각 null, undefinedtrue를 반환한다.
  • 문자와 숫자를 비교할 시, ToNumber을 통해 타입을 자로 강제 변환한 후 비교한다.
  • 문자와 BigInt를 비교할 시, StringToBigInt를 통해 문자를 BigInt로 변환한 후 비교한다. (7)
  • x 또는 y가 boolean이라면 이를 ToNumber을 통해 타입을 숫자로 강제 변환한 후 비교한다.
  • x 또는 y가 원시 자료형(String, Number, BigInt)이고, 다른 피연산자가 참조 자료형인 Object일 경우, ToPrimitive를 통해 Object를 원시 자료형으로 강제 변환한 후 비교한다.
  • x 또는 y가 BigInt고 다른 피연산자가 Number일 경우, 두 가지 조건(13-a, 13-b)에 따라 값을 반환한다.
  • 위에 해당되는 조건에 의해 true값이 반환되지 않을 경우, false를 반환한다.

https://tc39.es/ecma262/#sec-islooselyequal



2. IsStrictlyEqual

그렇다면 === 연산자는 비교를 위해 어떤 알고리즘을 따를까

ECMAScript 명세서를 참고해보면 해당 알고리즘을 따른다.

Untitled 1

  • x, y의 타입이 다를 경우 false를 반환한다.
  • x가 Number일 경우, Number::equal을 통해 값을 반환한다.
  • SameValueNonNumber을 통해 값을 비교하고 반환한다.

https://tc39.es/ecma262/#sec-isstrictlyequal




암묵적 타입 변환

타입이 강제로 변환되는 것은 암묵적 타입 변환(Implicit coercion)이라고도 한다.

이는 동등 연산자를 사용했을 때 뿐만 아닌, JS 엔진에 의해 암묵적으로 자동 변환되는 것을 말한다.

let x = 100;

let str = x + "";

console.log(typeof str); // string
console.log(x); // 100 (변수 x의 값은 변경되지 않는다.)

암묵적 타입 변환이 기존 값을 직접 변경하는 것이 아니다. JS 엔진이 표현식을 에러없이 평가하기 위해 기존 값을 바탕으로 새로운 타입의 값을 만들어 단 한번만 사용하고 버리게 된다.

위의 예제를 보면, x의 숫자 값을 바탕으로 새로운 문자열 ‘100’을 생성하고 이것으로 표현식 ‘100’ + ‘ ’을 평가한다. 이때 자동으로 생성된 문자열 ‘100’은 표현식의 평가가 끝나면 아무도 참조하지 않으므로 가비지 컬렉터에 의해 메모리에서 제거된다.



1. Context

JS 엔진은 표현식을 평가할 때 Context를 고려해 암묵적 타입 변환을 실행한다.

// 문자열 타입으로 변환
"10" + 5; // '105'

// 숫자 타입으로 변환
5 * "10"; // 50
5 - "10"; // -5
5 / "hello"; // NaN

!0; // true


1-1. 문자열 타입으로 변환

‘+’ 연산자는 피연산자 중 하나 이상이 문자열이므로 문자열 연결 연산자로 동작한다. (모든 피연산자가 숫자일 경우에는 더하기 연산자로 동작한다.)

문자열 연결 연산자는, 문자열 값을 만든다. 따라서 문자열 연결 연산자의 피연산자는 Context상 문자열 타입이어야 한다.

JS은 따라서 문자열 연결 연산자의 피연산자 중에서 문자열 타입이 아닌 피연산자를 문자열 타입으로 암묵적 타입 변환한다.


1-2. 숫자 타입으로 변환

산술 연산자를 사용하는 경우에는, 숫자 값을 만들어야 하므로 Context숫자 타입으로 암묵적 타입 변환하게 된다. 다만 피연산자를 숫자 타입으로 변환할 수 없는 경우에는 산술 연산을 수행할 수 없으므로 NaN을 반환한다.


1-3. Boolean 타입으로 변환

if문과 for문 제어문의 조건식은 Boolean 값을 반환해야 하는 표현식이다. 따라서 JS 엔진은 조건식의 평가 결과를 Boolean 타입으로 암묵적 타입 변환한다.

이때 JS 엔진은 불리언 타입이 아닌 값을 Truthy값(참으로 인식할 값) 또는 Falsy(거짓으로 인식할 값)으로 구분한다. 즉 Truthy값은 true로, Falsy값은 false로 변환된다.

아래 값들은 제어문의 조건식과 같이 불리언 값으로 평가되어야 할 컨텍스트에서 false로 평가되는 Falsy값이다.

  • false
  • undefined
  • null
  • 0, -0
  • NaN
  • ’’ (빈문자열)




명시적 타입 변환

명시적 타입 변환(Explicit coercion)개발자의 의도에 의해 타입을 변경하는 것이다.

해당 변환을 위한 방법은 다양하다. 래퍼 객체를 생성하기 위해 사용하는 래퍼 객체 생성자 함수를 new 연산자 없이 호출하는 방법과 자바스크립트에서 제공하는 빌트인 메소드를 사용하는 방법, 그리고 앞에서 살펴본 암묵적 타입 변환을 이용하는 방법이 있다.





출처

ECMAScript_2023_Language_Specification

Inside JavaScript(인사이드 자바스크립트)

https://poiemaweb.com/js-type-coercion

댓글남기기