본문 바로가기
JAVASCRIPT

[JS][강제변환] 암시적 강제변환 : * -> 불리언, || 와 && 연산자

by KBS 2022. 2. 11.
728x90

암시적 강제변환 : * -> 불리언

이번에 볼 것은 불리언 값으로의 암시적 강제변환이다. 가장 평범하면서도 가장 골칫거리다.

암시적 강제변환은 어떤 값이 강제로 바뀌어야 하는 방향으로 사용할 때 발생한다는 것을 기억하자. 숫자, 문자열 연산에서 일어나는 강제변환은 쉽게 알아챌 수 있다.

다음은 불리언으로의 강제변환이 일어나는 표현식을 열거한 것이다.

  1. if()문의 조건 표현식
  2. for( ; ; )에서 두 번째 조건 표현식
  3. while()do..while() 루프의 조건 표현식
  4. ? : 삼항 연산 시 첫 번째 조건 표현식
  5. ||&&의 좌측 피연산자.

이런 콘텍스트에서 불리언 아닌 값이 사용되면, ToBoolean 추상연산 규칙에 따라 일단 불리언 값으로 암시적 강제변환 된다.

var a = 42;
var b = "abc";
var c;
var d = null;

if (a) {
  console.log("넹"); // 넹
}

while (c) {
  console.log("실행이 불가하지용");
}

c = d ? a : b;
c; // "abc"

if ((a && d) || c) {
  console.log("넹"); // 넹
}

 

예제의 모든 콘텍스트에서 비 불리언 값은 조건 표현식을 평가하기 위해 그와 동등한 불리언 값으로 강제변환된다.

&& 와 || 연산자

||&& 연산자는 우리가 사용해온 대부분의 프로그래밍 언어에서 쉽게 찾아볼 수 있다. 그래서인지 자연스레 작동 방식도 별 차이가 없을 거라 생각한다.

그러나 여기에 아주 중요하면서도 미묘한 차이가 있다는 사실은 잘 모른다.

솔직히 이것들을 '논리 연산자'라고 부르는 건 명칭만으론 이 연산자들의 기능을 나타내기에 한참 부족하기 때문에 부적절하다고 본다. 나더러 작명을 하라고 한다면 '선택 연산자'또는 더 완전하게는 '피연산자 선택 연산자'정도로 할 것이다.

이유는 자바스크립트에서 이 두 연산자는 다른 언어와 달리 실제로 결괏값이 논리 값이 아니기 때문이다.

그럼 대체 결괏값은? 두피연산자중 한쪽 값이다. 즉, 두 피연산자의 값들 중 하나를 선택한다.

ES5 11.11에 명세님이 가라사대,

&& 또는 ||연산자의 결괏값이 반드시 불리언 타입이어야 하는 것은 아니며 항상 두 피연산자 표현식 중 어느 한쪽 값으로 귀결된다.

예를 들어 보자.

var a = 42;
var b = "abc";
var c = null;

a || b; // 42
a && b; // "abc"

c || b; // "abc"
c && b; // null

 

뭔가 이상하다..? 생각해보자. C, PHP 같은 언어라면 결괏값은 당연히 true 아니면 false다. 그런데 자바스크립트에서는(그리고 파이선, 루비) 결괏값이 피연산자 값이다.

||,&& 연산자는 우선 첫 번째 피연산자(a, c)의 불리언 값을 평가한다. ㅣㅍ연산자가 비 불리언 타입이면 먼저 ToBoolean로 강제변환 후 평가를 계속한다.

|| 연산자는 그결과가 true면 첫 번재 피연산자(a, c)값을, false면 두 번재 피연산자(b)값을 반환한다.

이와 반대로 && 연산자는 true면 두번째 피연산자의 값을, false면 첫 번재 피연산자의 값을 반환한다.

||, && 표현식의 결괏값은 언제나 피연산자의 값 중 하나이고, 평가 결과가 아니다. c && b에서 c는 null이므로 falsy한 값이다. 하지만 && 표현식은 평가 결과인 false가 아니라 c자신의 값인 null로 귀결된다.

앞에서 읻르을 '피연산자 선택 연산자'라고 부르려고 했던 이유를 이제 알겠는가?

a || b;
// 대략 다음과 같다
a ? a : b;

a && b;
a ? b : a;

 

개발자가 직접 손으로 코딩하기 보다는 자바스크립트 압축기에서더 많이 쓰는, 또 다른 관용 코드가 있다. &&연산자는 첫 번재 피연산자의 평가 결과가 truthy일 때만 두 번째 피연산자를 '선택'한다고 했는데 이런 특성을 '가드 연산자'라고 한다. 첫 번째 표현식이 두 번째 표현식의 '가드'역할을 한다.

function foo() {
  console.log(a);
}

var a = 42;

a && foo(); // 42

 

a 평가 결과가 truthy일 때에만 foo()가 호출된다. 평가 결과가 falsya && foo() 표현식은 그 자리에서 조용히 실행을 멈추고 foo()는 호출되지 않는다.

대부분의 경우 이런 식의 코딩보다는 조건문을 사용해 작성하는 사람들이 더 많을 것이다. 하지만 자바스크립트 압축기는 코드를 최대한 쥐어짜야 하므로 a && foo() 같이 처리한다. 그래서 만약 이렇게 생긴 코드를 해독할 일이 있으면 그 의미와 이유를 잘 알고 있어야 한다.

암시적 강제변환이라는 재료를 기꺼이 반북에 털어 넣을 분비가 됬다면 이제부터 ||와 &&가 묘기를 부릴것이다.

||, &&연산 결과가 실제로 true/false가 아니라는 사실이 우리에겐 불편할 수도 있다. 지금까지 우리가 작성해온 a && (b || c)같은 복합 논리 표현식이 포함된 if문이나 for루프는 도대체 어떻게 작동했던 것일까 궁금할 것이다.

걱정하지 말자. 하늘이 무너질 일은 없다. 우리늬 코드는 계속 문제없이 돌아갈 것이다. 다만 먼저 복합 표현식이 평가된 다음 불리언으로 암시적 강제변환이 일어난다는 사실을 몰랐을 것이다.

var a = 42;
var b = null;
var c = "foo";

if (a && (b || c)) {
  console.log("넹~");
}

 

이 코드는 우리가 짐작한 대로 작동하지만 절묘한 게 한 가지 있다. a && (b || c) 표현식의 실제 결과는 true가 아닌 "foo"다. if문은 이 "foo"를 불리언 타입으로 강제변환할여 true로 만든다.

알겠는가? 당황할 필요가 없다. 우리가 작성해온 코드는 안전하다 무슨 일이 어떻게 하는지, 내면의 진실의 눈을 뜨게 되었을 뿐이다.

그리구 우리는 이런 코드가 암시적 강제변환을 사용하고 있다는 것을 깨달았다. 우리가 아직도 암시적 강제변환 사용 금지 클럽의 열성 회원이라면, 아마 다음과 같이 명시적인 평가 표현식을 작성해야 잘했다고 칭찬할 것이다.

if (!!a && (!!b || !!c)) {
  console.log("으잉!");
}

ㅋㅋㅋㅋㅋ..


참고

  • You Don't Know JS - 타입과 문법, 스코프와 클로저( 한빛 미디어 )
728x90

댓글