객체 프로퍼티 키 평가 과정

Lee Dogeon @moreal@hackers.pub

AI도 무조건 틀리는 Javascript 퀴즈 (Hackers' Pub 글)을 풀어보면서 알게된 것을 적어보는 글입니다. 사실... 알고보니 간단한 곳에서 끝나는 이야기였지만 그래도 메모삼아 남겨놓습니다 🫠


예제 코드 돌려보기

원 글의 코드를 인용하면 아래와 같습니다.

const age = {
    ruby: 18,
    ayumu: 19,
    siki: 20
}

const preferences = {
    ruby: ["mint chocolate", "you"],
    ayumu: ["strawberry", "you"],
    siki: ["cookie and cream", "you"]
}

["ruby", "ayumu", "siki"].forEach(printAge)

function printAge(name) {
    console.log(`${name} is ${age[name]}`)
}

일단 눈으로 볼 때는 ruby is 18 같은 꼴로 쭉 나올 것 같습니다. 그래서 뭐지 해서 돌려보니 예상과 다르게 아래와 같은 결과가 나왔습니다.

cookie and cream is undefined
you is undefined

처음에는 당황했는데 세미콜론이 없어 {...}[...].forEach(...) 같은 꼴로 처리가 된 것이었습니다.

Symbol.toPrimitive

세미콜론이 없어서 {...}[...] 같은 꼴로 이어 붙은 것은 이해했지만 ["ruby", "amuyu", "siki"] 같이 여러 값이 주어지는데 이게 요소에 접근이 되는 것이 잘 이해가 가지 않았습니다. 그래서 코드를 이래저래 만지다가 다른 것을 찾아봤는데 결론부터 이야기하면 자바스크립트에는 comma가 튜플로 동작하지 않고, comma로 이어진 표현식들 중 마지막 표현식의 평가값이 리턴되도록 해서 동작하므로 "siki"가 키로 사용되어 관련 값들이 출력된 것이었습니다.[1]

> console.log((1, 2, 3))
3

아무튼 이걸 인지 못 하고 별개로 코드를 만져보다가 아래와 같은 코드도 동작하는 것을 알게 되었습니다.

console.log({foo: 1, bar: 2}[[[[[["foo"]]]]]]) // 1
console.log({foo: 1, bar: 2}[[[["foo"]]], [[[["bar"]]]]]) // 2

배열이 프로퍼티 키로 들어갈 수 있다는 것이 다소 이해가 가지 않았습니다. 들어갈 수는 있겠으나 그에 상응하는 값이 없을테니 undefined를 반환하는 것이 맞다고 생각했습니다. 관련하여 명세가 어떻게 되어있는지 찾아봤습니다.

찾아본 과정을 기록하고 싶어서 링크를 모두 나열해봤는데 너무 길어져서 접어놓았습니다.
  1. https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#sec-property-accessors
    프로퍼티에 접근하는 방식에 대한 문법을 설명합니다.
  2. https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#sec-property-accessors-runtime-semantics-evaluation
    프로퍼티에 접근하는 것이 런타임에서 어떻게 동작해야 하는지 설명합니다.
  3. https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#sec-evaluate-property-access-with-expression-key
    EvaluatePropertyAccessWithExpressionKey의 동작
  4. https://tc39.es/ecma262/multipage/ecmascript-data-types-and-values.html#sec-getvalue
    GetValue의 동작
    1. https://tc39.es/ecma262/multipage/ecmascript-data-types-and-values.html#property-key
      property key term에 대한 설명
    2. https://tc39.es/ecma262/multipage/abstract-operations.html#sec-topropertykey
      ToPropertyKey의 동작
      1. https://tc39.es/ecma262/multipage/abstract-operations.html#sec-toprimitive
        ToPrimitive 동작

간단히 적으면 ...[key] 꼴로 프로퍼티에 접근할 때 keyproperty key로 변환하는데 이때 Symbol.toPrimitive가 있다면 이를 활용하고 아니라면 toString, valueOf를 사용합니다. 자바스크립트에서는 [["foo"]].toString()의 값은 "foo"와 같으므로 위 예제 코드가 동작하는 것입니다.

Symbol.toPrimitive를 활용하면 객체가 property key로 사용될 때 객체가 어떻게 평가되어야 하는지 정의할 수 있습니다. 아래와 같이 사용하면 "foo" 프로퍼티에 접근할 수 있습니다.

> console.log({foo: 1, bar: 2}[{ [Symbol.toPrimitive]() { return "foo" } }]);
1

마무리 및 관련 자료

정답(?)은 간단한 거였고 관련 없는 걸 찾아본 꼴이 되었지만 새로운 걸 알게된 게 좋아서 메모 겸 적어보았습니다. 그리고 위 같은 기묘한 코드는 사실 유효한 동작이면 안 되는 것 같지만, 아마 제가 모르는 용도가 어딘가 있을거라 생각하며 마무리 지어봅니다.


  1. https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#sec-comma-operator ↩︎

6

No comments

If you have a fediverse account, you can comment on this article from your own instance. Search https://hackers.pub/ap/articles/0197fb65-7580-74c2-bc22-9ca264e1f7d1 on your instance and reply to it.