티스토리 뷰

4.6 Optional chaining '?.'

종종 어떤 값을 가져오려 할 때, 해당 값이 존재하지 않는 경우가 있다. 

let user = {}; // a user without "address" property

alert(user.address.street); // Error!

이 경우 에러를 뱉는 것보다, undefined와 같은 값이 반환되는 것이 좋을 것 같다.

let user = {};

alert(user.address ? user.address.street : undefined);

// ---------------------------------

let user = {}; // user has no address

alert(user.address ? user.address.street ? user.address.street.name : null : null)

위와 같이 해당 값의 존재여부를 체크하고 undefined를 반환하도록 할 수 있지만, 같은 구문이 반복되기도 하고 보기가 좋지는 않다. 이럴 때 사용하는 것이 optional chainig이다.

Optional chaining

let user = {}; // user has no address

alert( user?.address?.street ); // undefined (no error)

user가 존재 => user.address 접근, user.address가 존재 => user.address.street로 접근한다. 만약 중간에 값이 없는(null이나 undefined) 경우 undefined를 반환한다.

 

만약 아래처럼 일부 대상에게만 있다면 해당 대상에게만 적용된다.

let user = null;

alert( user?.address ); // undefined
alert( user?.address.street ); // undefined

위 예시처럼, optional chaining이 유용하긴하지만 비즈니스적으로 optional한 대상에 한해 사용하는 것이 적절해보인다. 값이 무조건 존재해야하는 대상인데 없는 경우라면, undefined를 반환하기보다 에러를 뱉는 것이 어쩌면 더 바람직할 수 있기 때문이다.

 

Other variants: ?.(), ?.[]

아래와 같이 함수 존재여부, 변수 속성 존재 여부, 삭제 전 존재여부를 판별할 때도 사용될 수 있다.

let userAdmin = {
  admin() {
    alert("I am admin");
  }
};

let userGuest = {};

userAdmin.admin?.(); // I am admin

userGuest.admin?.(); // nothing happens (no such method)


// -----------------------------
let key = "firstName";

let user1 = {
  firstName: "John"
};

let user2 = null;

alert( user1?.[key] ); // John
alert( user2?.[key] ); // undefined

// -----------------------------

delete user?.name; // delete user.name if user

하지만, 값을 새롭게 할당하는 작업에는 사용할 수 없다.

let user = null;

user?.name = "John"; // Error, doesn't work
// because it evaluates to: undefined = "John"

4.7 symbols

객체 속성의 키는 string, symbol 타입만이 가능하다. symbol 타입은 아래와 같이 생성 가능하다. 고유한 식별자로서 역할을 하기 때문에, 같은 이름으로 symbol을 생성하더라도 서로 다른 객체로 취급한다.

// id is a symbol with the description "id"
let id = Symbol("id");

// --------------
let id1 = Symbol("id");
let id2 = Symbol("id");

alert(id1 == id2); // false

또한, symbol은 자동으로 string으로 형변환되지 않는다.

symbol을 생성 후 alert 함수로 symbol을 출력하려해도 문자열로 변환되지 않기 때문에 에러가 발생한다. 필요하다면 .toString()을 사용해야 한다.

"Hidden" properties 

let user = { // belongs to another code
  name: "John"
};

let id = Symbol("id");

user[id] = 1;

alert( user[id] ); // we can access the data using the symbol as the key

symbol 타입은 우연히 접근하거나 덮어쓸 수 없는 숨겨진 객체를 만들 수 있다. 위 예시를 보면 user 객체의 id로 symbol 타입을 사용했다. 해당 속성이 쉽게 변경될 수 없도록 symbol로 생성한 것이다.

symbols in an object literal

symbol로 이루어진 속성은 반복문이나, Object.keys(user)와 같은 함수를 통해서도 접근할 수 없다. 

Global symbols

보통 같은이름의 symbol이어도 다른 것으로 취급하지만, 원한다면 같은 이름을 가지면 같은 symbol 취급을 하도록 만들 수 있다. (아래 코드 참조)

// read from the global registry
let id = Symbol.for("id"); // if the symbol did not exist, it is created

// read it again (maybe from another part of the code)
let idAgain = Symbol.for("id");

// the same symbol
alert( id === idAgain ); // true

Symbol.keyFor

아래 방식으로 symbol의 이름을 반환한다. 다만, global symbol이 아닌 경우 동작하지 않는다.

/ get symbol by name
let sym = Symbol.for("name");
let sym2 = Symbol.for("id");

// get name by symbol
alert( Symbol.keyFor(sym) ); // name
alert( Symbol.keyFor(sym2) ); // id

// ------------------------------

let globalSymbol = Symbol.for("name");
let localSymbol = Symbol("name");

alert( Symbol.keyFor(globalSymbol) ); // name, global symbol
alert( Symbol.keyFor(localSymbol) ); // undefined, not global

alert( localSymbol.description ); // name

System symbols

  • Symbol.hasInstance
  • Symbol.isConcatSpreadable
  • Symbol.iterator
  • Symbol.toPrimitive

위 System symbol 들은 자주 쓰인다고 한다. 참고로 기억해 두자.

댓글
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday