티스토리 뷰

이번 ES2020에서 새로 추가된 Optional Chaining은 "?."를 사용하여 객체의 속성 값에 대해 접근할 수 있게 해줍니다.

 

이미 타입스크립트 3.7 버전에서 구현된 기능이기 때문에, 기존에 타입스크립트를 사용하시던 개발자분들은 그리 낯설지 않은 문법일 것 입니다.

 

참조하는 대상이 null이나 undefined가 아니라면(= nullish 하지 않다면) 속성에 대한 접근이 가능하며, 만약 nullish 할 경우 undefined를 반환합니다.

function getProp(obj) {
  return obj?.a;
}

// A
const obj = {
  a: 1
};

getProp(obj); // 1

// B
const obj2 = null;

getProp(obj2); // undefined

 

일반적인 객체 형식이 아닌 함수의 호출에도 역시 사용이 가능하며, 방법은 동일합니다.

function add(x, y) {
  return x + y;
}

const num = add?.(2, 3);

console.log(num); // 5

 

함수가 존재한다면 실행하고 아니면 undefined를 반환합니다.

 

자바스크립트에서는 런타임시에 프로퍼티를 체크하기 때문에

const user = {
  name: 'Pewww',
  gender: 'male'
};

// A와 B 모두 Uncaught TypeError: Cannot read property 'zonecode' of undefined 발생

// A
const {
  address: {
    zonecode
  }
} = user;

// B
const zonecode = user.address.zonecode;

와 같이 user의 address가 존재하지 않는 상황에서 zonecode에 접근할 경우 에러를 출력하며, 이를 방지하기 위해

const {address} = user;
const {zonecode} = address || {};

const zonecode = user.address && user.address.zonecode;

와 같은 코드를 작성합니다.

 

접근해야 할 대상이 많지 않을 땐 크게 신경쓰지 않아도 되지만, 객체의 중첩이 아래와 같이 깊어질 경우 구문은 지저분해질 수 밖에 없고 곧 가독성에 타격을 줍니다.

const obj = {
  a: {
    b: {
      c: {
        d: {
          e: 'Hello'
        }
      }
    }
  }
};

// Before
const e = obj.a && obj.a.b && obj.a.b.c && obj.a.b.c.d && obj.a.b.c.d.e;

// After
const e = obj?.a?.b?.c?.d?.e;

다소 뜬금 없을 수도 있지만, Optional Chaining이 적용된 코드가 Babel을 통해 transpiling을 거쳤을 때 어떻게 변화하는지 확인하고 싶어 테스트를 해봤습니다.

const obj = {
  a: 1
};

const a = obj?.a;

다음과 같은 코드가

const obj = {
  a: 1
};

const a = obj == null ? void 0 : obj.a;

 

의 형태로 변화하는 것을 보고 문득 void 0은 무엇이고 어떤식으로 동작을 하는지, undefined 대신 사용된 이유가 무엇일지 궁금해졌습니다.


void는 연산자로써 뒤에 나타내는 표현식을 실행하고 undefined를 반환합니다.

 

보통 구문은 아래와 같이 void expression의 형태를 띄고,

void console.log(5); // 콘솔에 5 출력 후 undefined 반환
void 120 + 110; // 120 + 110 계산 후 undefined 반환

 

괄호의 유무의 따라 주어진 식의 평과 과정이 달라질 수 있습니다.

// undefined == '10'과 같음, 따라서 false 반환
void 10 == '10';

// void true와 같음, 따라서 undefined 반환
void (10 == '10');

 

 IIFE를 사용할 때 void를 사용하면, function 키워드를 선언문이 아닌 표현식 처럼 간주하도록 강제하는 것 역시 가능합니다.

void function ceilNum(num) {
  const result = Math.ceil(num);
  console.log(result);
}(5.3);

// 콘솔에 6 출력

 

a 태그에 링크 기능을 제거하기 위해 사용하기도 하지만,

<a href="javascript:void(0)">
  https://pewww.tistory.com
</a>

 

Mozila에서 이벤트 처리기와 같은 대체제를 사용하라는 것으로 봐선 javascript: 의사 프로토콜의 사용을 그리 권장하진 않는 듯 합니다.

 

그렇다면 왜 Babel은 undefined가 아닌 void 0을 사용한 것일까요?


undefined는 전역 객체의 속성이며, 초기 값은 undefined 그 자체의 원시 값입니다.

 

최신 브라우저에서 undefined는 ECMAScript 5 명세에 따라 설정과 쓰기가 불가능한 속성이지만, 오래된 브라우저에서는 전역에서의 값의 할당이 가능했습니다.

 

물론 현재도 undefined는 예약어(https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Lexical_grammar#키워드) 가 아니기 때문에 지역 변수로써 선언될 수는 있지만, 역시 좋은 방법은 아니며 반드시 피해야 하는 구문이라고 MDN은 설명하고 있습니다.

function compareUndefined() {
  const undefined = 5;
  const num = 5;

  return num === undefined;
}

const result = compareUndefined();

console.log(result); // true

 

이러한 특징들을 봤을 때, babel이 void 연산자를 사용한 이유는 아마 이 점이 아닐까 싶습니다.

 

"Undefined 그 자체로서의 의미를 보장하기 위해."

 

즉, 의미에 대한 불변성의 유지를 위한 것이죠.

상황에 따라 나타내는 값이 달라질 수 있는 undefined 대신 void 0을 사용함으로써, 데이터 (= transpile된 결과물) 의 정확성을 보장하고 혹시 모를 예외나 변수들을 미리 차단하는 것으로 보입니다.

 

물론, 단순히 개인적인 의견이기 때문에 실제 의도와 다를 수 있다는 점 알아주셨으면 합니다... 😄


어쨌든 이번 ES2020에 꽤나 흥미로운 문법들이 많이 추가되었던데, 잘 적응해서 실제 코드에 활용해봐야겠습니다.

글 읽어주셔서 감사합니다 :D

댓글