ES6 In Depth: 컬렉션 (Collections)

ES6 In Depth는 ECMAScript 표준의 6번째 에디션(줄여서 ES6)을 맞아 JavaScript에 새로 추가된 요소들을 살펴보는 시리즈입니다.

이번 주 초에(2015년 6월 19일 기준), ECMA-262 6th Edition, ECMAScript 2015 Language Specification 이라고 명명된 ES6 스펙이 마지막 난관을 극복하고 Ecma 표준으로 공식 승인되었습니다. 표준 제정에 기여한 TC39 와 모든 사람들에게 축하를 전합니다. 이제 ES6 는 공식 문서가 되었습니다!

더 좋은 소식이 있습니다. 다음 번 표준 에디션은 6년보다 짧은 시간 안에 발표될 것입니다. 표준화 위원회는 이제 대략 12개월마다 한 번씩 새로운 표준 에디션을 만들기로 목표를 정했습니다. 7번째 에디션을 위한 제안이 벌써 진행 중입니다.

이런 상황에서, 제가 그토록 오랫동안 JS 에서 보고 싶어했던, 그리나 아직도 개선의 여지가 있는 기능에 대해 이야기해 보는 것도 좋을 것 같습니다!

공진화(coevolution)가 어려운 랭귀지

JS 는 다른 프로그래밍 랭귀지와 매우 다릅니다. 이런 독특함이 랭귀지의 진화에 놀랄만한 영향을 줍니다.

ES6 모듈(Module)이 그 좋은 예입니다. 다른 랭귀지들은 모듈 시스템을 가지고 있습니다. Racket 도 멋진 모듈 시스템을 갖고 있습니다. Python 도 그렇습니다. 표준화 위원회가 ES6 에 모듈 시스템을 추가하기로 결정했을 때, 왜 다른 랭귀지에 있는 모듈 시스템을 그냥 가져오지 못했을까요?

JS 는 웹브라우저에서 실행되기 때문에 다른 랭귀지들과 다릅니다. I/O 처리를 위해 긴 시간이 필요합니다. 그래서 JS 에는 코드를 비동기적으로 로딩할 수 있는 모듈 시스템이 필요합니다. JS 의 경우에는 여러 디렉토리에 존재하는 모듈들을 순차적으로 탐색하는 모듈 시스템도 사용할 수 없습니다. 기존 모듈 시스템을 그냥 가져오는 것이 아무 도움 되지 않는 상황입니다. ES6 모듈 시스템은 무언가 다른 방식으로 동작해야 합니다.

JS 의 이런 독특함이 모듈 시스템의 최종 설계에 미친 영향도 재미있는 주제입니다. 하지만 지금 이야기할 주제는 아닙니다.

이 글은 ES6 의 “key 를 갖는 컬렉션 (keyed collection)”에 대한 것입니다. 즉 Set, Map, WeakSet, WeakMap 에 대한 글입니다. 컬렉션은 다른 랭귀지의 해시 테이블 (hash table)과 비슷합니다. 하지만 표준화 위원회는 JS 의 독특함 때문에 컬렉션을 구현하는 과정에서 흥미로운 선택을 해야 했습니다.

왜 컬렉션이죠?

JS 에 친숙한 사람이라면 JS 랭귀지에 이미 해시 테이블과 비슷한 것이 존재한다는 사실을 알고 있을 것입니다. 객체(Object) 말입니다.

일반적인 Object 는 결국 key-value 쌍을 끊임 없이 추가할 수 있는 컬렉션일 뿐입니다. 당신은 객체의 속성을 읽고, 쓰고, 삭제하고, 탐색할 수 있습니다. 해시 테이블로 할 수 있는 것처럼 말이죠. 그런데 왜 컬렉션이 필요한 걸까요?

많은 프로그램들이 key-value 쌍의 저장을 위해 일반 객체를 사용합니다. 일반 객체로 충분한 프로그램이라면 Map 이나 Set 으로 전향할 필요 없습니다. 하지만 일반 객체로 해결할 수 없는 문제들이 있습니다.

  • 검색 테이블(lookup table)로 사용 중인 객체에 메소드를 추가하려면 이름 충돌의 위험을 감수해야 합니다.

  • 그래서 프로그램을 짤 때, (쉬운 {} 표기법 보다) Object.create(null) 을 사용해야 하고, 또 (Object.prototype.toString 같은) 빌트인(builtin) 메소드를 멤버 데이터로 혼돈하지 않도록 주의해야 합니다.

  • 속성 key 는 언제나 문자열입니다 (ES6 에서는 심볼도 가능합니다). 속성 key 로 객체를 사용할 수 없습니다.

  • 객체에 얼마나 많은 속성이 존재하는지 알아낼 수 있는 효과적인 방법이 존재하지 않습니다.

ES6 에는 새로운 고민이 존재합니다. 일반 객체는 이터러블(iterable) 하지 않습니다. 그래서 forof 루프 구문이나, ... 구문을 사용할 수 없습니다.

다시 한번 말하지만, 세상에는 그런 문제들에 구애되지 않는 경우들도 아주 많습니다. 그런 경우에는 일반 객체가 올바른 선택입니다. MapSet 은 다른 경우를 위한 선택입니다.

ES6 컬렉션이 사용자 데이터와 빌트인 메소드 사이의 이름 충돌을 피하기 위해 설계되었기 때문에, ES6 컬렉션은 자신의 멤버 데이터를 드러내기 위해 속성(property)을 사용하지 않습니다. 이는 해시 테이블의 멤버 데이터에 접근하기 위해 obj.key 또는 obj[key] 같은 구문을 사용할 수 없다는 것을 의미합니다. 반드시 map.get(key) 구문을 이용해야 합니다. 또, 해시 테이블의 멤버 데이터는 속성과 달리 프로퍼티 체인(property chain)을 통해 상속되지 않습니다.

이로 인한 장점이라면, Object 와 달리 MapSet 에는 자유롭게 메소드를 추가할 수 있다는 점입니다. 이름 충돌의 위험 없이 객체에 메소드를 추가할 수 있습니다.

Set

Set 은 value 들로 이루어진 컬렉션입니다. Set 은 수정 가능합니다. 그래서 프로그램이 실행되는 동안 Set 에 value 를 추가하거나 삭제할 수 있습니다. 여기까지는 배열과 같습니다. 하지만 set 과 배열 사이에는 유사점만큼 많은 차이점이 있습니다.

먼저, 배열과 달리, set 에는 같은 value 가 2번 포함될 수 없습니다. set 안에 이미 존재하는 값을 한번 더 추가하려고 시도하면 아무 일도 일어나지 않습니다.

> var desserts = new Set("????");
> desserts.size
    4
> desserts.add("?");
    Set [ "?", "?", "?", "?" ]
> desserts.size
    4

이 예제에서는 문자열을 사용했지만, Set 에는 모든 JS 타입의 value 를 담을 수 있습니다. 문자열의 경우처럼 객체나 숫자를 1번 이상 추가할 경우 아무 일도 일어나지 않습니다

둘째로, Set 은 뚜렷한 목적을 가지고 데이터를 관리합니다. 바로 어떤 데이터가 자신의 멤버인지 확인하는 작업을 빨리 처리하려는 목적입니다.

> // Check whether "zythum" is a word.
> arrayOfWords.indexOf("zythum") !== -1  // slow
    true
> setOfWords.has("zythum")               // fast
    true

Set 으로 할 수 없는 일도 있습니다. Set 은 인덱스 값으로 데이터를 조회하는 일을 할 수 없습니다.

> arrayOfWords[15000]
    "anapanapa"
> setOfWords[15000]   // sets don't support indexing
    undefined

set 과 관련된 모든 작업 목록은 다음과 같습니다.

  • new Set 구문은 비어 있는 새로운 set 을 만듭니다.

  • new Set(iterable) 구문은 새로운 set 을 만들고 인자로 전달된 이터러블(iterable)로 데이터를 채웁니다.

  • set.size 구문은 set 안에 담겨 있는 데이터의 개수를 조회합니다.

  • set.has(value) 구문은 주어진 값이 set 안에 존재할 경우 true 를 리턴합니다.

  • set.add(value) 구문은 주어진 값을 set 에 추가합니다. 만약 그 값이 이미 set 안에 존재하면 아무 일도 일어나지 않습니다.

  • set.delete(value) 구문은 set 에서 주어진 값을 제거합니다. 만약 그 값이 set 안에 존재하지 않으면 아무 일도 일어나지 않습니다. .add() 구문과 .delete() 구문은 모두 set 객체 자신을 리턴합니다. 따라서 구문을 체인(chain) 시킬 수 있습니다.

  • set[Symbol.iterator]() 구문은 set 안의 값들을 순회할 수 있는 새로운 이터레이터를 리턴합니다. 보통의 경우 이 메소드를 직접 호출할 일은 없습니다. 하지만 이 메소드의 존재 때문에 set 은 이터러블(iterable) 합니다. 즉, for (v of set) {...} 같은 구문을 쓸 수 있습니다.

  • set.forEach(f) 구문은 코드로 설명하는 것이 쉽습니다. 이 구문은 다음 코드를 짧게 쓴 것입니다.

    for (let value of set)
        f(value, value, set);
    

    이 메소드는 array 의 .forEach() 메소드와 비슷합니다.

  • set.clear() 구문은 set 안의 모든 데이터를 제거합니다.

  • set.keys(), set.values(), set.entries() 구문은 다양한 이터레이터들을 리턴합니다. 이 이터레이터들은 Map 과의 호환성을 위해 제공됩니다. 이에 대해서는 조금 뒤에 다시 다루겠습니다.

이런 모든 특성들 중에서, new Set(iterable) 생성자 구문이 가장 강력합니다. 왜냐하면 이 구문은 모든 종류의 데이터 구조들 위에서 동작하기 때문입니다. 이 생성자 구문을 이용하면 array 를 set 으로 변환하는 것도 가능하고, 프로그램 코드 라인에서 중복되는 문자열을 제거하는 것도 가능합니다. 이 생성자 구문에 제너레이터(generator)를 전달할 수도 있습니다. 그럴 경우 이 생성자 구문은 제너레이터를 끝까지 실행시켜서 yield 된 값들을 set 안에 저장할 것입니다. 이 생성자 구문은 이미 존재하는 Set 을 복제하는 방법이기도 합니다.

지난 글에서 저는 새로 추가된 ES6 컬렉션에 대한 불평도 약속했습니다. 이제 불평을 시작하겠습니다. Set 이 훌륭하기는 하지만, 누락된 메소드들이 있습니다. 나중에라도 표준에 추가되면 좋겠습니다.

  • array 에 이미 존재하는 함수형 헬퍼 구문들이 누락됐습니다. .map(), .filter(), .some(), .every() 같은 구문들 말입니다.

  • 내용이 변하지 않는 set1.union(set2) 구문과 set1.intersection(set2) 구문이 누락됐습니다.

  • 많은 값들을 한꺼번에 처리할 수 있는 메소드들이 누락됐습니다. set.addAll(iterable), set.removeAll(iterable), set.hasAll(iterable) 같은 구문들 말입니다.

좋은 소식이 있다면 ES6 의 메소드들을 이용해서 이 모든 것들을 효과적으로 구현할 수 있다는 것입니다.

Map

Map 은 key-value 페어(pair)로 이루어진 컬렉션입니다. Map 으로 할 수 있는 일들은 다음과 같습니다.

  • new Map 구문은 비어 있는 새로운 map 을 만듭니다.

  • new Map(pairs) 구문은 새로운 map 을 만들고 그 데이터를 기존의 [key, value] 페어 컬렉션으로 채웁니다. pairs 인자는 기존의 Map 객체일 수도 있고, 2개의 엘리먼트를 가진 배열들로 구성된 배열일 수도 있으며, 2개의 엘리먼트를 yield 하는 제너레이터일 수도 있습니다.

  • map.size 구문은 map 에 담겨진 엔트리들의 개수를 조회합니다.

  • map.has(key) 구문은 주어진 key 가 존재하는지 확인합니다 (key in obj 구문처럼요).

  • map.get(key) 구문은 key 와 연관된(associated) value 를 리턴합니다. 만약 그런 엔트리가 존재하지 않을 경우 undefined 를 리턴합니다 (obj[key] 구문처럼요).

  • map.set(key, value) 구문은 map 에 keyvalue 엔트리를 추가합니다. 같은 key 를 갖는 엔트리가 이미 존재할 경우 기존의 데이터를 덮어 씁니다. (like obj[key] = value 구문처럼요).

  • map.delete(key) 구문은 엔트리를 삭제합니다. (delete obj[key] 구문처럼요).

  • map.clear() 구문은 map 안의 모든 엔트리들을 제거합니다.

  • map[Symbol.iterator]() 구문은 map 안의 엔트리들을 순회할 수 있는 이터레이터를 리턴합니다. 해당 이터레이터는 엔트리 항목 각각을 [key, value] 배열로 표현합니다.

  • map.forEach(f) 구문은 다음과 같이 동작합니다.

    for (let [key, value] of map)
      f(value, key, map);
    

    인자 순서가 이상한 것은, 다시 한번 말하지만, Array.prototype.forEach() 구문과 통일성을 유지하기 위해서입니다.

  • map.keys() 구문은 map 안의 key 들을 순회할 수 있는 이터레이터를 리턴합니다.

  • map.values() 구문은 map 안의 value 들을 순회할 수 있는 이터레이터를 리턴합니다.

  • map.entries() 구문은 map 안의 모든 엔트리들을 순회할 수 있는 이터레이터를 리턴합니다. map[Symbol.iterator]() 구문과 똑 같습니다. 사실 이것은 동일한 메소드에 대한 다른 명칭일 뿐입니다.

여기에 대해서도 불평할 것이 있을까요? 유용할 것이라고 생각됨에도 불구하고 ES6 에 포함되지 않은 기능들이 있습니다.

  • Python의 collections.defaultdict 처럼 디폴트 값을 부여하는 기능이 누락됐습니다.

  • 오브젝트-리터럴 문법으로 map 을 쉽게 정의할 수 있는 헬퍼 펑션 Map.fromObject(obj) 이 누락됐습니다.

하지만, 이 기능들도 쉽게 구현할 수 있습니다.

좋습니다. 제가 어떻게 이 글을 시작했는지 기억해 보세요. 저는 브라우저에서 실행된다는 독특한 문제가 JS 언어의 설계에 미치는 영향을 이야기했었습니다. 기억하나요? 지금 바로 그점에 대해 좀 더 이야기해 보려고 합니다. 3개의 예제를 준비했습니다. 우선 2개의 예제를 소개합니다.

JS 는 다릅니다. 파트 1: 해시 코드 없는 해시 테이블?

제가 알기로는, 유용함에도 불구하고 ES6 컬렉션이 전혀 지원하지 않는 기능이 하나 있습니다.

우리에게 URL 객체로 이루어진 Set 이 있다고 가정해 봅시다.

var urls = new Set;
urls.add(new URL(location.href));  // two URL objects.
urls.add(new URL(location.href));  // are they the same?
alert(urls.size);  // 2

이 2개의 URL 객체들은 똑같은 것으로 취급되어야 옳습니다. 이 두 객체는 똑같은 속성 값을 갖습니다. 하지만, JavaScript 에서는 두 객체가 다릅니다. 그리고 랭귀지가 정한 동등함의 판정 기준을 바꿀 방법이 없습니다.

다른 랭귀지들은 이 기능을 지원합니다. Java, Python, Ruby 같은 랭귀지들은 클래스를 정의할 때 비교 연산자를 오버로딩할 수 있습니다. Scheme 랭귀지 구현체들의 경우, 해시 테이블이 저마다 다른 비교 연산자를 사용할 수 있습니다. C++ 은 2가지 방식을 모두 지원합니다.

하지만, 이들 메카니즘이 동작하기 위해서는 사용자가 특별한 해시 함수를 구현해야 합니다. 그래서 관련 랭귀지들은 모두 시스템 차원의 디폴트 해시 함수를 공개하고 있습니다. 표준화 위원회는 (적어도 지금까지는) JS 에서 해시 코드를 공개하지 않기로 결정했습니다. 이 결정은 상호운용성(interoperability)과 보안(security)에 대한 고민 때문입니다. 다른 랭귀지에서는 상호운용성과 보안에 대한 고민이 JS 만큼 절실하지 않습니다.

JS 는 다릅니다. 파트 2: 놀랍습니다! 예측 가능성!

아마도 컴퓨터가 예측 가능한 결과를 만든다는 사실이 당연한 것 아니냐고 느끼실 겁니다. 하지만 MapSet 을 순회(iteration)할 때 엔트리의 순회 순서가 엔트리의 추가 순서와 같다는 사실을 알려주면 많은 사람들이 놀랍니다. 컬렉션의 순회 순서가 예측 가능합니다.

우리는 해시 테이블의 순회 순서가 임의적이라는 사실에 익숙해져 있습니다. 우리는 임의적인 순회 순서를 받아들이도록 배워왔습니다. 하지만 임의적인 순회 순서를 피할만한 충분한 이유가 있습니다. 2012년에 다음과 같은 글을 쓴 적이 있습니다.

  • 어떤 프로그래머들의 경우 임의적인 순회 순서를 처음 접할 때 놀라거나 당혹스러워 한다는 증거가 있습니다. [1][2][3][4][5][6]
  • 속성을 순회하는 순서는 ECMAScript 표준에 규정되어 있지 않습니다. 하지만 주요 브라우저들은 기존 웹과의 호환성을 고려해서 속성이 추가된 순서대로 속성을 순회합니다. 이로 인한 고민이 존재합니다. 만약 TC39 가 순회 순서를 정하지 않는다면 “웹은 그냥 하던 대로 할 것입니다. 결국 순회 순서 역시 하던 대로 암묵적으로 규정할 것입니다”.[7]
  • 해시 테이블의 순회 순서는 객체의 해시 코드를 노출할 수 있습니다. 이것은 해시 코드 생성 함수를 구현할 때 중요한 보안 문제를 야기합니다. 예를 들어, 해시 코드로부터 절대 어떤 객체의 주소를 유추할 수 있어서는 안됩니다. (신뢰할 수 없는 ECMAScript 코드에 객체의 주소가 노출되면, 설사 그자체로는 악용될 여지가 없다하더라도, 웹에서는 아주 나쁜 보안적 결함입니다.)

지난 2012년 2월에 이 모든 것들이 논의됐을 때, 저는 임의적인 순회 순서를 지지하는 입장이었습니다. 당시 저는 아이템의 추가 순서를 추적하면 해시 테이블이 무척 느려질 수 있다는 사실을 실험을 통해 보여주려고 했습니다. 저는 C++ 로 간단한 벤치마크 코드를 작성했습니다. 하지만, 벤치마크 결과는 제게 충격이었습니다.

그리고 그것이 JS 해시 테이블이 아이템의 추가 순서를 추적하기로 결론 내린 까닭입니다!

약한(weak) 컬렉션을 사용해야 하는 강한 이유

지난 글에서, 우리는 JS 애니메이션 라이브러리에 관한 예제를 놓고 토론했습니다. 우리는 모든 DOM 객체마다 불린 플랙(boolean flag)을 저장하려고 했습니다. 다음 코드처럼 말이죠.

if (element.isMoving) {
  smoothAnimations(element);
}
element.isMoving = true;

불행히도, 이런 방식으로 DOM 객체에 속성을 추가하는 것은 나쁜 생각입니다. 지난 글에서 그 이유를 설명했습니다.

지난 글에서는 심볼을 이용한 문제 해결 방식을 설명했습니다. 하지만 Set 을 이용해서도 같은 일을 처리할 수 있지 않을까요? 다음 코드처럼 말이죠.

if (movingSet.has(element)) {
  smoothAnimations(element);
}
movingSet.add(element);

여기에는 단 하나의 단점이 있습니다. MapSet 객체는 멤버로 포함하는 모든 key 와 value 를 강력하게 참조합니다 (key 와 value 에 대한 strong reference 를 유지합니다). 이는 만약 DOM 엘리먼트가 DOM 문서에서 제거되더라도 movingSet 컬렉션에서 제거되지 않으면 가비지컬렉터가 메모리를 회수할 수 없다는 것을 의미합니다. 사용자에게 복잡한 방식으로 “쓰고-난-다음에-정리하라”고 요구하는 라이브러리는 기껏해야 절반의 성공만 거둘 수 있습니다. 이 방식은 메모리 릭(leak)을 만들 가능성이 높습니다.

ES6 는 이 문제에 대한 놀라운 해결책을 제공합니다. movingSetSet 이 아니라 WeakSet 으로 만드는 것입니다. 그러면 메모리 릭이 해결됩니다!

위크-컬렉션(weak collection)도 심볼(symbol)처럼 이런 특별한 문제의 해결책이 될 수 있다는 뜻입니다. 어느 것이 더 나을까요? 2가지 방법의 장단점을 완벽히 논하자면 불행히도 이 글이 무척 길어질 것입니다. 만약 웹 페이지가 존재하는 동안 1개의 심볼만 사용하는 상황이라면 나쁘지 않습니다. 하지만 만약 짧은 시간 존재하는 심볼이 여러 개 필요한 상황이라면 그건 위험 신호입니다. 그럴 땐 메모리 릭을 피하는 방법으로 WeakMap 사용을 고려해보세요.

WeakMapWeakSet

스펙에 의하면 WeakMapWeakSetMapSet 과 동일하게 동작하도록 정의되어 있습니다. 아주 약간의 제한만 존재합니다.

  • WeakMapnew, .has(), .get(), .set(), .delete() 만 지원합니다.

  • WeakSetnew, .has(), .add(), .delete() 만 지원합니다.

  • WeakSet 에 저장되는 value 와 WeakMap 에 저장되는 key 는 반드시 객체여야 합니다.

2가지 위크-컬렉션(weak collection)이 모두 이터러블(iterable)이 아니라는 사실을 일러둡니다. 필요한 엔트리를 분명하게 명시하지 않으면 위크-컬렉션 안의 엔트리를 얻을 수 없습니다. 필요한 엔트리를 얻으려면 필요한 key 를 전달해야 합니다.

이것은 심사숙고하여 의도적으로 부여된 제한입니다. 이 제한 덕분에 가비지컬렉터는 위크-컬렉션의 존재와 무관하게 소멸된 객체를 수거할 수 있습니다. 그 효과는 느슨한 레퍼런스(weak reference)나 느슨한 딕셔너리(weak-keyed dictionary)를 사용해서 얻는 효과와 비슷합니다. 하지만 ES6 의 위크-컬렉션은 스크립트에게 GC 실행 사실을 노출하지 않고서도 메모리를 관리할 수 있는 장점이 있습니다.

JS 는 다릅니다. 파트3: GC 실행의 예측 불가능성 숨기기

내부를 들여다보면 위크-컬렉션은 ephemeron table 로 구현되어 있습니다.

간단히 말해, WeakSet 은 자신의 멤버 객체에 대한 레퍼런스를 보관하지 않습니다. WeakSet 내부의 멤버 객체가 메모리로부터 제거되면, WeakSet 에서도 제거됩니다. WeakMap 도 비슷합니다. WeakMap 은 자신의 멤버 key 들에 대한 레퍼런스를 보관하지 않습니다. key 가 살아 있을 경우에만 관련 value 가 유지됩니다.

왜 이런 제약을 용인한 걸까요? 왜 그냥 JS 에 위크-레퍼런스(weak reference)를 추가하지 않은 걸까요?

이번에도 표준화 위원회는 스크립트에 불확정적인 동작양식(nondeterministic behavior)이 포함되는 것을 두려워한 것입니다. 크로스-브라우저 호환성이 떨어지는 것은 웹 개발 측면에서 큰 문제입니다. 위크-레퍼런스는 가비지컬렉터 구현에 대한 세세한 내용을 노출합니다. 가비지컬렉터의 구현 방식은 플랫폼마다 제각각일 수 밖에 없는 임의적인 부분입니다. 어플리케이션은 당연히 플랫폼마다 제각각인 특징에 의존하면 안됩니다. 위크-레퍼런스를 쓸 경우, 개발자의 코드가 현재 테스트 중인 브라우저의 GC 행동양식에 얼마나 의존하고 있는지 파악하기 힘듭니다. 그것은 논리적으로 따지기 매우 힘든 일입니다.

대조적으로, ES6 의 위크-컬렉션은 비교적 제한된 기능만 가지고 있지만, 그 기능들 자체는 단단하게 규정되어 있습니다. 우리는 key 또는 value 가 메모리로부터 제거(GC)됐다는 사실을 미리 예측할 수 없습니다. 따라서 어플리케이션은 우연히라도 그 사실(GC 실행여부)에 의존하면 안됩니다.

이것은 웹에만 적용되는 문제가 설계 상의 결정을 이끌어낸 한가지 사례입니다. 이런 결정 때문에 JS 는 더 좋은 랭귀지가 됐습니다.

컬렉션을 언제부터 쓸 수 있나요?

4가지 컬렉션 클래스들 모두가 현재 Firefox, Chrome, Microsoft Edge, Safari 에서 지원됩니다. 오래된 브라우저에서 컬렉션을 쓰려면 es6-collections 같은 폴리필을 쓰세요.

WeakMap 은 Andreas Gal 에 의해 처음 Firefox 위에 구현됐습니다. 그는 잠깐 동안 Mozilla 의 CTO 였습니다. WeakSet 은 Tom Schuster 가 구현했습니다. 저는 MapSet 을 구현했습니다. Tooru Fujisawa 에게 감사를 전합니다. 그는 여러번 패치를 만들어 기여했습니다.

지금까지 ES6 In Depth 시리즈를 통해 기본적인 내용을 다뤘습니다. ES6 의 가장 강력한 기능들은 아직 다루지 않았습니다. 다음 글을 기대해 주세요.

이 글은 Jason Orendorff가 쓴 ES6 In Depth: Collections의 한국어 번역본입니다.

작성자: ingeeKim

"누구에게나 평등하고 자유로운 웹"에 공감하는 직장인.

ingeeKim가 작성한 문서들…


댓글이 없습니다.

댓글 쓰기