JavaScript에서 맵과 객체


290

방금 chromestatus.com을 발견하고 하루 동안 몇 시간을 잃은 후이 기능 항목을 발견 했습니다 .

맵 : 맵 객체는 간단한 키 / 값 맵입니다.

그것은 나를 혼란스럽게했다. 일반 JavaScript 객체는 사전이므로 사전과 Map다른 점은 무엇입니까? 개념적으로는 동일합니다 ( 지도와 사전의 차이점은 무엇입니까? )

chromestatus 참조 문서는 다음 중 어느 것도 도움이되지 않습니다.

맵 객체는 키와 값이 임의의 ECMAScript 언어 값일 수있는 키 / 값 쌍의 모음입니다. 고유 한 키 값은지도 모음 내에서 하나의 키 / 값 쌍에서만 발생할 수 있습니다. 맵 작성시 선택되는 비교 알고리즘을 사용하여 식별 된 고유 키 값.

Map 객체는 요소를 삽입 순서대로 반복 할 수 있습니다. 맵 객체는 해시 테이블 또는 평균적으로 컬렉션의 요소 수에 대한 하위 선형 액세스 시간을 제공하는 다른 메커니즘을 사용하여 구현해야합니다. 이 Map 객체 사양에 사용 된 데이터 구조는 Map 객체의 필수 관찰 의미를 설명하기위한 것입니다. 실행 가능한 구현 모델이 아닙니다.

… 여전히 나에게 물체처럼 들리므로 분명히 무언가를 놓쳤습니다.

JavaScript가 (잘 지원되는) Map객체를 얻는 이유는 무엇 입니까? 무엇을합니까?


답변:


286

모질라에 따르면 :

Map 객체는 삽입 순서대로 요소를 반복 할 수 있습니다. for..of 루프는 각 반복에 대해 [키, 값] 배열을 반환합니다.

객체는지도와 유사하여 키를 값으로 설정하고, 값을 검색하고, 키를 삭제하고, 키에 무언가가 저장되어 있는지 여부를 감지 할 수 있습니다. 이 때문에 객체는 역사적으로지도로 사용되었습니다. 그러나지도를 더 잘 사용하도록하는 객체와지도에는 중요한 차이점이 있습니다.

객체에는 프로토 타입이 있으므로 맵에 기본 키가 있습니다. 그러나 map = Object.create (null)을 사용하여 무시할 수 있습니다. 객체의 키는 문자열이며지도의 값이 될 수 있습니다. 오브젝트의 크기를 수동으로 추적해야하는 동안 맵의 크기를 쉽게 얻을 수 있습니다.

런타임까지 키를 알 수없는 경우와 모든 키가 동일한 유형이고 모든 값이 같은 유형 인 경우 오브젝트에 대한 맵을 사용하십시오.

개별 요소에서 작동하는 논리가있는 경우 개체를 사용하십시오.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map

순서대로 반복성은 모든 브라우저에서 동일한 성능을 보장하기 때문에 개발자가 오랫동안 원했던 기능입니다. 나에게 그것은 큰 것입니다.

myMap.has(key)방법은 특히 유용하며 myMap.size속성 도 있습니다.


13
아마도 단점은 삽입 순서를 유지하기 위해 Map에 더 많은 메모리가 필요하다는 것입니다 (동일한 크기 내에서).
John Kurlak

4
지도에는 여기에 언급 된 순서와는 다른 기능 (키로 객체 사용, 키와 소품 분리 등)이 있지만, 경우에 따라 FWIW는 일반 객체 속성의 반복 순서가 ES2015에 의해 정의됩니다. stackoverflow.com/a/32149345를 참조하십시오 .
JMM

2
당신이 말할 때, Object는 프로토 타입을 가지고 있으므로 맵에 기본 키가 있다는map = Object.create(null) 의미를 얻지 못했습니다 . 그러나을 사용하여 무시할 수 있습니다 . 기본 키란 무엇입니까? 키는 어떻게 관련되어 Object.prototype있습니까?
overexchange

4
Chrome에서 테스트 한 결과지도에서 순서를 유지하기 위해 더 많은 양의 메모리를 사용하지 않는 것으로 나타났습니다. 나는 백만 개의 키에 대해 0.1KB가 더 많았고 순서를 유지하기위한 것이라고 생각하지 않습니다. 그러나 ~ 0.1KB는 일정한 오버 헤드로 보입니다. 대신 하나의 키로 백만 개의 맵을 만들고 비교하면 객체보다 훨씬 큽니다.
jgmjgm 2016 년

2
@luxon 거기에 객체를 만들고 있습니다. ES6 사양에서는 new연산자를 Map심볼 과 함께 new Map사용하여 맵 객체를 생성해야합니다. var a = {}의 줄임말 (와 동등한 의미)var a = Object.create(Object.prototype)
dudewad

104

주요 차이점은지도에서 거의 모든 키 유형을 지원하는 경우 객체는 문자열 키만 지원한다는 것입니다.

내가 할 경우 obj[123] = true다음과 Object.keys(obj)그때 얻을 것 ["123"]보다는 [123]. 지도는 키의 유형을 유지하고 [123]훌륭합니다. 지도를 사용하면 객체를 키로 사용할 수도 있습니다. 전통적으로 이렇게하려면 객체를 해시하기 위해 일종의 고유 식별자를 객체에 제공해야합니다 ( getObjectIdJS에서 표준의 일부로 본 적이 없다고 생각 합니다). 지도는 또한 주문 보존을 보장하므로 보존에 더 적합하며 때로는 몇 가지 종류의 작업을 수행 할 필요가 없습니다.

실제로지도와 객체 사이에는 몇 가지 장단점이 있습니다. 객체는 JavaScript의 핵심에 매우 밀접하게 통합되어 장점과 단점을 모두 얻을 수 있으며, 이는 주요 지원의 차이를 뛰어 넘어 Map과 크게 차별화됩니다.

즉각적인 이점은 객체를 구문 적으로 지원하여 요소에 쉽게 액세스 할 수 있다는 것입니다. 또한 JSON으로 직접 지원합니다. 해시로 사용하면 속성이없는 객체를 얻는 것이 귀찮습니다. 기본적으로 객체를 해시 테이블로 사용하려는 경우 객체가 오염되어 hasOwnProperty속성에 액세스 할 때 종종 해당 객체를 호출 해야합니다. 기본적으로 객체가 오염되는 방법과 해시로 사용하기 위해 희망적으로 오염되지 않은 객체를 만드는 방법을 볼 수 있습니다.

({}).toString
    toString() { [native code] }
JSON.parse('{}').toString
    toString() { [native code] }
(Object.create(null)).toString
    undefined
JSON.parse('{}', (k,v) => (typeof v === 'object' && Object.setPrototypeOf(v, null) ,v)).toString
    undefined

객체에 대한 오염은 코드를 더 성가 시게하거나 느리게 만들뿐만 아니라 보안에 잠재적 인 영향을 줄 수 있습니다.

객체는 순수한 해시 테이블이 아니지만 더 많은 노력을 기울이고 있습니다. 같은 두통 hasOwnProperty이 있고 길이를 쉽게 얻을 수 없습니다 ( Object.keys(obj).length) 등. 객체는 순수하게 해시 맵으로 사용되는 것이 아니라 동적 확장 가능한 객체로도 사용되므로 순수한 해시 테이블 문제가 발생할 때 사용할 수 있습니다.

다양한 일반적인 작업 비교 / 목록 :

    Object:
       var o = {};
       var o = Object.create(null);
       o.key = 1;
       o.key += 10;
       for(let k in o) o[k]++;
       var sum = 0;
       for(let v of Object.values(m)) sum += v;
       if('key' in o);
       if(o.hasOwnProperty('key'));
       delete(o.key);
       Object.keys(o).length
    Map:
       var m = new Map();
       m.set('key', 1);
       m.set('key', m.get('key') + 10);
       m.foreach((k, v) => m.set(k, m.get(k) + 1));
       for(let k of m.keys()) m.set(k, m.get(k) + 1);
       var sum = 0;
       for(let v of m.values()) sum += v;
       if(m.has('key'));
       m.delete('key');
       m.size();

다양한 기복 (성능, 간결함, 휴대용, 확장 가능 등)과 함께 몇 가지 다른 옵션, 접근 방식, 방법론 등이 있습니다. 객체는 언어의 핵심이되는 약간 이상하기 때문에 작업을위한 많은 정적 메소드가 있습니다.

키 유형을 유지하는지도의 장점 외에도 객체와 같은 것들을 객체가 갖는 부작용으로부터 격리 된 키로 지원할 수 있다는 장점이 있습니다. 지도는 순수한 해시로, 동시에 객체가 되려는 것에 대한 혼란이 없습니다. 프록시 기능으로 맵을 쉽게 확장 할 수도 있습니다. 객체는 현재 프록시 클래스를 가지고 있지만 성능 및 메모리 사용량은 심각합니다. 실제로 객체에 대한 맵처럼 보이는 자체 프록시를 만드는 것은 현재 프록시보다 성능이 뛰어납니다.

지도의 실질적인 단점은 JSON에서 직접 지원되지 않는다는 것입니다. 구문 분석이 가능하지만 몇 가지 끊기가 있습니다.

JSON.parse(str, (k,v) => {
    if(typeof v !== 'object') return v;
    let m = new Map();
    for(k in v) m.set(k, v[k]);
    return m;
});

위의 내용은 성능에 심각한 영향을 미치며 문자열 키도 지원하지 않습니다. JSON 인코딩은 훨씬 어렵고 문제가 많습니다 (여러 가지 접근 방식 중 하나임).

// An alternative to this it to use a replacer in JSON.stringify.
Map.prototype.toJSON = function() {
    return JSON.stringify({
        keys: Array.from(this.keys()),
        values: Array.from(this.values())
    });
};

순전히지도를 사용하는 경우 그렇게 나쁘지는 않지만 유형을 혼합하거나 스칼라 이외의 값을 키로 사용할 때 문제가 발생합니다 (JSON은 IE 원형 객체 참조와 같은 문제에 완벽하지는 않습니다). 테스트하지는 않았지만 stringify에 비해 성능이 크게 저하 될 수 있습니다.

다른 스크립팅 언어는 종종 Map, Object 및 Array에 대해 스칼라가 아닌 명시 적 유형을 갖는 것과 같은 문제가 없습니다. 웹 개발은 종종 스칼라가 아닌 유형에 어려움을 겪습니다 .PHP는 속성에 A / M을 사용하여 객체와 배열 / 맵을 병합하고 JS는 배열 확장 M / O와 맵 / 객체를 병합합니다. 복잡한 유형을 병합하는 것은 고급 스크립팅 언어에 대한 악마의 골칫거리입니다.

지금까지는 구현과 관련하여 주로 문제가되었지만 기본 작업의 성능도 중요합니다. 엔진과 사용량에 따라 성능도 복잡합니다. 실수를 배제 할 수 없기 때문에 소금 한 알로 시험을 치십시오 (이걸 서 두어야합니다). 또한 매우 간단한 시나리오 만 검토하여 대략적인 지표 만 제시하는지 확인하기 위해 자체 테스트를 실행해야합니다. 매우 큰 객체 / 맵에 대한 Chrome의 테스트에 따르면 삭제로 인해 객체의 성능이 좋지 않습니다. 삭제는 O (1)이 아닌 키 수에 비례합니다.

Object Set Took: 146
Object Update Took: 7
Object Get Took: 4
Object Delete Took: 8239
Map Set Took: 80
Map Update Took: 51
Map Get Took: 40
Map Delete Took: 2

Chrome은 가져오고 업데이트하는 데 큰 장점이 있지만 삭제 성능은 끔찍합니다. 이 경우 맵은 오버 헤드를 조금만 사용하지만 오버 헤드가 수백만 키로 테스트되는 객체 / 맵이 하나만 있으면 맵 오버 헤드의 영향이 잘 표현되지 않습니다. 메모리 관리를 사용하면 프로파일을 올바르게 읽고 있으면 객체를 선호하는 이점이있을 수 있습니다.

이 특정 벤치 마크에 대한 Firefox에서는 다른 이야기입니다.

Object Set Took: 435
Object Update Took: 126
Object Get Took: 50
Object Delete Took: 2
Map Set Took: 63
Map Update Took: 59
Map Get Took: 33
Map Delete Took: 1

Firefox의 객체 에서이 특정 벤치 마크를 삭제해도 문제가 발생하지 않지만 다른 벤치 마크에서는 특히 Chrome과 같이 많은 키가있을 때 문제가 발생했음을 즉시 지적해야합니다. 대규모 컬렉션의 경우 Firefox에서 맵이 확실히 우수합니다.

그러나 이것은 이야기의 끝이 아닙니다. 많은 작은 물체 나지도는 어떻습니까? 나는 이것에 대한 빠른 벤치 마크를했지만 위의 작업에서 적은 수의 키로 가장 잘 수행되는 철저한 벤치 마크 (설정 / 가져 오기)가 아닙니다. 이 테스트는 메모리와 초기화에 관한 것입니다.

Map Create: 69    // new Map
Object Create: 34 // {}

다시이 수치는 다양하지만 기본적으로 Object는 좋은 리드를 가지고 있습니다. 어떤 경우에는 맵에서 오브젝트의 리드가 극단적 (~ 10 배 더 낫습니다)이지만 평균적으로 2-3 배 정도 더 좋습니다. 극단적 인 성능 스파이크가 양방향으로 작동 할 수있는 것 같습니다. 메모리 사용량과 오버 헤드를 프로파일 링하기 위해 Chrome에서만 생성하고 테스트했습니다. Chrome에서 하나의 키를 가진지도가 하나의 키를 가진 객체보다 약 30 배 더 많은 메모리를 사용하는 것으로 나타났습니다.

위의 모든 작업 (4 키)으로 많은 작은 개체를 테스트하려면 다음을 수행하십시오.

Chrome Object Took: 61
Chrome Map Took: 67
Firefox Object Took: 54
Firefox Map Took: 139

메모리 할당 측면에서 이들은 해제 / GC 측면에서 동일하게 동작했지만 Map은 5 배 더 많은 메모리를 사용했습니다. 이 테스트는 마지막 테스트 에서처럼 하나의 키만 설정 한 4 개의 키를 사용하여 메모리 오버 헤드 감소를 설명합니다. 이 테스트를 몇 번 실행했으며 Map / Object는 전체 속도 측면에서 Chrome의 전반적인 목과 목입니다. 작은 오브젝트 용 Firefox에서는 전체 맵에 비해 확실한 성능 이점이 있습니다.

물론 여기에는 크게 다를 수있는 개별 옵션이 포함되어 있지 않습니다. 나는이 수치를 미세 최적화하는 조언을하지 않을 것입니다. 이를 극복 할 수있는 것은 경험상 엄청나게 큰 키 값 저장소 및 작은 키 값 저장소의 오브젝트에 대해 맵을 더 강력하게 고려한다는 것입니다.

이 두 가지를 사용하는 가장 좋은 전략을 넘어서서 구현하고 먼저 작동하게하십시오. 프로파일 링 할 때 가끔 객체 키 삭제 사례에서 볼 수 있듯이 엔진 문제로 인해 느릴 것으로 생각하지 않는 것이 느려질 수 있음을 명심해야합니다.


직렬화의 부족은 많은 개발자들에게 큰 고통이었습니다. 의 upvote에 봐 내가 (다른 나) 로컬 스토리지에 ES6지도를 지속 어떻게해야합니까? 그리고 어떻게 ES6 맵을 JSON.stringify합니까? .
Franklin Yu

숫자는 밀리 초, 바이트 또는 총 객체입니까?
StefansArya

ms를 얻었습니다 (사용 된 것을 말하는 데 걸리는 것이 짧기 때문에이 경우에는 시간이 걸립니다). 이것은 오래된 테스트이지만 더 이상 벤치 마크 코드가 없습니다. 아마 지금 매우 다를 것입니다. 예를 들어 삭제 문제가 해결되었습니다.
jgmjgm

27

나는 지금까지 답변에서 다음 사항을 언급하지 않았다고 생각하며 언급 할 가치가 있다고 생각했습니다.


지도가 더 클 수 있습니다

크롬에서 내가 얻을 수있는 16.7 와 만 키 / 값 쌍 Map11.1 일반 개체 만. 와 거의 정확히 50 % 더 많은 쌍 Map. 둘 다 충돌하기 전에 약 2GB의 메모리를 차지하므로 크롬에 의한 메모리 제한과 관련이 있다고 생각합니다 ( 편집 : 예, 2를 채우 Maps십시오. 이 코드로 직접 테스트 할 수 있습니다 (명확하게 동시에 실행하지 말고 별도로 실행하십시오).

var m = new Map();
var i = 0;
while(1) {
    m.set(((10**30)*Math.random()).toString(36), ((10**30)*Math.random()).toString(36));
    i++;
    if(i%1000 === 0) { console.log(i/1000,"thousand") }
}
// versus:
var m = {};
var i = 0;
while(1) {
    m[((10**30)*Math.random()).toString(36)] = ((10**30)*Math.random()).toString(36);
    i++;
    if(i%1000 === 0) { console.log(i/1000,"thousand") }
}

객체에는 이미 몇 가지 속성 / 키가 있습니다

이 사람은 전에 나를 트립했다. 일반 객체가 toString, constructor, valueOf, hasOwnProperty, isPrototypeOf등의 기존 특성의 무리. 이것은 대부분의 사용 사례에서 큰 문제는 아니지만 이전에 문제를 일으켰습니다.

지도가 느려질 수 있습니다.

.get함수 호출 오버 헤드와 내부 최적화 부족 으로 인해 일부 작업의 경우 맵 이 일반 오래된 JavaScript 객체보다 상당히 느려질 수 있습니다 .


1
당신의 의견으로는, 의미론이 여기서 성능보다 중요합니까? 사전이 필요한 경우지도가 완벽하게 들리지만 조회 속도가 느리기는 어렵습니다. 사전의 전체 요점을 빠르게 조회하지 않습니까?
user2954463

3
당신이 1100 만 키 / 값 쌍 나왔습니다 벌금과 같은 기존의 키를 신경 쓰지 않으면 나는 확실히 평범한 오래된 개체를 가고 싶어 toString, constructor등 (즉, 열쇠는 그들과 충돌 매우 어렵다). 예를 들어 증가입니다 - 그들은 작업하기 쉬운 것 obj[i] = (obj[i] || 0) + 1와 반면에, Map그것은의 map.set(i, (map.get(i) || 0) + 1)여전히 나쁘지하지 않은,하지만 그냥 쇼 일들이 불필요하게 혼란스러워 할 수있는 방법. 지도에는 분명히 사용 사례가 있지만 종종 일반 객체가 사용합니다.

1
기본 제거 할 수 있습니다 toString, constructor작성하여, (등) 개체 속성 obj = Object.create(null)대신 obj = {}.

18

Javascript는 동적으로 유형이 지정되므로 언제든지 객체의 속성을 추가하거나 제거 할 수 있으므로 객체는 사전처럼 작동 할 수 있습니다.

그러나 다음과 같은 Map()이유로 새로운 기능이 훨씬 좋습니다.

  • 그것은 제공 get, set, has, 및 delete방법을.
  • 문자열 대신 모든 유형의 키를 허용합니다.
  • for-of사용 하기 쉬운 반복자를 제공하고 결과 순서를 유지합니다.
  • 반복 또는 복사 중에 프로토 타입 및 기타 속성이 표시되는 엣지 케이스가 없습니다.
  • 수백만 개의 항목을 지원합니다.
  • 자바 스크립트 엔진이 향상됨에 따라 매우 빠르며 계속 빨라집니다.

시간의 99 %는을 사용해야합니다 Map(). 그러나 문자열 기반 키만 사용하고 최대 읽기 성능이 필요한 경우 객체를 선택하는 것이 더 좋습니다.

세부 사항은 (거의 모든) 자바 스크립트 엔진이 백그라운드에서 C ++ 클래스로 객체를 컴파일한다는 것입니다. 이러한 유형은 "개요"에 의해 캐시되고 재사용되므로 동일한 속성을 가진 새 객체를 만들면 엔진이 기존 백그라운드 클래스를 재사용합니다. 이러한 클래스의 속성에 대한 액세스 경로는 매우 최적화되어 a의 조회보다 훨씬 빠릅니다 Map().

속성을 추가하거나 제거하면 캐시 된 백업 클래스가 다시 컴파일되므로 키 추가 및 삭제가 많은 사전으로 개체를 사용하는 것이 매우 느리지 만 개체를 ​​변경하지 않고 기존 키를 읽고 할당하는 것이 매우 빠릅니다.

따라서 문자열 키를 사용하여 한 번만 읽은 읽기 작업량이 많은 object경우 특수한 고성능 사전으로 사용하지만 다른 모든 경우에는를 사용하십시오 Map().


객체는 get set has delete기능성도 제공 하지만 그렇게 우아하지는 않습니다 (그러나 나쁘지는 않습니다). Map반복에 어떤 방법 을 사용 하는 것이 더 쉬운가요? 동의 할 수 있는지 잘 모르겠습니다.
앤드류

@Andrew 나는 방법을 취하고 있는데, 사용중인 것과 결과에 따라 기능이 다릅니다. 프로토 타입 및 기본 속성이 루프에 표시되지 않고 동일한 순서를 유지하는 일반적인 JS 반복기를 사용하기 때문에 반복이 더 쉽습니다.
마니 간담

11

다른 답변 외에도지도가 객체보다 다루기 쉽고 장황한 것으로 나타났습니다.

obj[key] += x
// vs.
map.set(map.get(key) + x)

더 짧은 코드는 더 빨리 읽고, 더 직접적으로 표현 하며, 프로그래머의 머리에 더 잘 유지 되기 때문에 이것은 중요 합니다.

또 다른 측면 : set ()은 값이 아닌 맵을 반환하기 때문에 할당을 연결하는 것은 불가능합니다.

foo = obj[key] = x;  // Does what you expect
foo = map.set(key, x)  // foo !== x; foo === map

지도 디버깅도 더 힘들다. 아래에서는 실제로지도에 어떤 키가 있는지 볼 수 없습니다. 그렇게하려면 코드를 작성해야합니다.

Map Iterator를 평가하는 행운을 빕니다

모든 IDE에서 객체를 평가할 수 있습니다.

객체 평가 WebStorm


4
이 모든 것을 감안할 때 map은 조기 최적화 인 것 같습니다.
PRMan

10

요약:

  • Object: 데이터가 키 값 쌍으로 저장되는 데이터 구조. 객체에서 키는 숫자, 문자열 또는 기호 여야합니다. 이 값은 다른 객체, 함수 등도 될 수 있습니다. 객체는 정렬되지 않은 데이터 구조입니다. 즉 키 값 쌍의 삽입 순서는 기억되지 않습니다
  • ES6 Map: 데이터가 키 값 쌍으로 저장되는 데이터 구조. 있는 고유 키 값에 매핑 . 키와 값은 모두 모든 데이터 유형이 될 수 있습니다 . 맵은 반복 가능한 데이터 구조입니다. 즉, 삽입 순서가 기억되고 for..of루프 등 의 요소에 액세스 할 수 있습니다.

주요 차이점 :

  • A Map는 정렬되고 반복 가능하지만 객체는 정렬되지 않고 반복 가능하지 않습니다

  • 모든 유형의 데이터를 Map키로 넣을 수 있지만 객체는 숫자, 문자열 또는 기호 만 키로 가질 수 있습니다.

  • 에서 Map상속 Map.prototype합니다. 이것은 모든 종류의 유틸리티 기능과 속성을 제공하여 Map객체 작업을 훨씬 쉽게 만듭니다.

예:

목적:

let obj = {};

// adding properties to a object
obj.prop1 = 1;
obj[2]    =  2;

// getting nr of properties of the object
console.log(Object.keys(obj).length)

// deleting a property
delete obj[2]

console.log(obj)

지도:

const myMap = new Map();

const keyString = 'a string',
    keyObj = {},
    keyFunc = function() {};

// setting the values
myMap.set(keyString, "value associated with 'a string'");
myMap.set(keyObj, 'value associated with keyObj');
myMap.set(keyFunc, 'value associated with keyFunc');

console.log(myMap.size); // 3

// getting the values
console.log(myMap.get(keyString));    // "value associated with 'a string'"
console.log(myMap.get(keyObj));       // "value associated with keyObj"
console.log(myMap.get(keyFunc));      // "value associated with keyFunc"

console.log(myMap.get('a string'));   // "value associated with 'a string'"
                         // because keyString === 'a string'
console.log(myMap.get({}));           // undefined, because keyObj !== {}
console.log(myMap.get(function() {})) // undefined, because keyFunc !== function () {}

출처 : MDN


4

또한 잘 정의 된 순서로 반복 가능하고 임의의 값을 키로 사용하는 기능 (제외 -0)을 사용하면 다음과 같은 이유로 맵이 유용 할 수 있습니다.

  • 이 스펙은 맵 조작이 평균적으로 서브 리니어되도록 강제합니다.

    바보가 아닌 객체 구현은 해시 테이블 또는 이와 유사한 것을 사용하므로 속성 조회는 평균적으로 일정 할 것입니다. 그러면 객체가지도보다 훨씬 빠를 수 있습니다. 그러나 이것은 사양에 필요하지 않습니다.

  • 개체에 예기치 않은 동작이 발생할 수 있습니다.

    예를 들어, foo속성을 새로 만든 object로 설정하지 않았 obj으므로 obj.fooundefined를 반환 한다고 가정 해 봅시다 . 그러나 foo에서 상속받은 기본 제공 속성 일 수 있습니다 Object.prototype. 또는 obj.foo할당을 사용하여 만들려고 하지만 Object.prototype값을 저장하는 대신 일부 세터가 실행됩니다.

    지도는 이런 종류의 것들을 막습니다. 글쎄, 일부 스크립트가 엉망이되지 않는 한 Map.prototype. 그리고 Object.create(null)작동하지만 간단한 객체 이니셜 라이저 구문이 손실됩니다.


4

일반 자바 스크립트 객체 대신지도를 사용하는시기

일반 JavaScript 객체 {키 : 'value'}는 구조화 된 데이터를 보유합니다. 그러나 일반 JS 객체에는 한계가 있습니다.

  1. 문자열과 기호 만 객체의 키로 사용할 수 있습니다. 다른 것들, 예를 들어 숫자를 객체의 키로 사용하면 해당 키에 액세스하는 동안 해당 키가 문자열로 변환되어 암시 적으로 유형의 일관성을 잃게됩니다. const names = {1 : 'one', 2 : 'two'}; Object.keys (이름); // [ '1', '2']

  2. JS 식별자를 객체의 키 이름 (예 : toString, 생성자 등)으로 작성하여 프로토 타입에서 상속 된 속성을 실수로 덮어 쓸 가능성이 있습니다.

  3. 다른 객체를 객체의 키로 사용할 수 없으므로 해당 객체를 다른 객체의 키로 작성하고 다른 객체의 값에 추가 정보가 포함되어 객체에 대한 추가 정보를 쓸 수 없습니다.

  4. 객체는 반복자가 아니다

  5. 물체의 크기는 직접 결정할 수 없습니다

이러한 객체 제한 사항은지도에서 해결되지만 Google은지도를 대체 대신 객체를 보완하는 것으로 간주해야합니다. 기본적으로 Map은 단지 배열의 배열이지만 배열의 배열에 대해서만 new 키워드를 사용하여 배열의 배열을 Map 객체에 인수로 전달해야합니다. 그렇지 않으면 배열의 배열에 대해서만 유용한 속성 및 방법을 사용할 수 없습니다. 그리고 배열 배열 내부의 키-값 쌍을 기억하십시오. 그렇지 않으면 일반 객체처럼 콜론없이 쉼표로만 맵을 구분해야합니다.

지도를 사용할지 또는 객체를 사용할지 결정하는 3 가지 팁 :

  1. 런타임에 키를 알 수없는 경우 사용자 입력으로 생성 된 키가 객체의 상속 된 속성을 덮어 쓰는 경우 객체를 사용하는 코드를 무의식적으로 깨뜨릴 수 있으므로 객체에 대한 맵을 사용하십시오. 따라서 이러한 경우 맵이 더 안전합니다. 모든 키가 동일한 유형이고 모든 맵이 동일한 유형 인 경우에도 맵을 사용하십시오.

  2. 기본 값을 키로 저장해야하는 경우 맵을 사용하십시오.

  3. 개별 요소를 조작해야하는 경우 객체를 사용하십시오.

지도 사용의 이점은 다음과 같습니다.

1. Map은 모든 키 유형을 허용하고 키 유형을 유지합니다.

객체의 키가 문자열이나 기호가 아닌 경우 JS는이를 암시 적으로 문자열로 변환합니다. 반대로, Map은 문자열, 숫자, 부울, 기호 등 모든 유형의 키를 허용하며 Map은 원래 키 유형을 유지합니다. 여기서는 맵에서 숫자를 키로 사용하고 숫자로 유지합니다.

const numbersMap= new Map();

numbersMap.set(1, 'one');

numbersMap.set(2, 'two');

const keysOfMap= [...numbersMap.keys()];

console.log(keysOfMap);                        // [1, 2]

맵 안에서 전체 객체를 키로 사용할 수도 있습니다. 희박한 객체로 작업 할 수 있지만 객체에 대한 일부 정보를 저장하기 위해 객체 자체에이 데이터를 첨부하지 않고 객체 관련 데이터를 저장하려는 경우가있을 수 있습니다. 이 경우 객체를 키로 사용하고 객체의 관련 데이터를 값으로 만들 수 있도록 맵을 사용해야합니다.

const foo= {name: foo};

const bar= {name: bar};

const kindOfMap= [[foo, 'Foo related data'], [bar, 'Bar related data']];

그러나이 방법의 단점은 원하는 값을 얻기 위해 전체 배열을 반복해야하기 때문에 키로 값에 액세스하는 것이 복잡하다는 것입니다.

function getBy Key(kindOfMap, key) {
    for (const [k, v]  of kindOfMap) {
        if(key === k) {
            return v;
        }
    }
    return undefined;
}

getByKey(kindOfMap, foo);            // 'Foo related data'

적절한 맵을 사용하여 값에 직접 액세스하지 못하는이 문제를 해결할 수 있습니다.

const foo= {name: 'foo'};

const bar= {name: 'bar'};

const myMap= new Map();

myMap.set(foo, 'Foo related data');
myMap.set(bar, 'Bar related data');

console.log(myMap.get(foo));            // 'Foo related data'

WeakMap을 사용 하여이 작업을 수행 할 수 있었고 const myMap = new WeakMap ()을 작성해야합니다. Map과 WeakMap의 차이점은 WeakMap은 키 (여기서는 객체)의 가비지 수집을 허용하므로 메모리 누수를 방지하고 WeakMap은 객체 만 키로 허용하며 WeakMap은 메소드 세트를 줄였습니다.

2.지도는 키 이름에 제한이 없습니다.

일반 JS 객체의 경우 실수로 프로토 타입에서 상속 된 속성을 덮어 쓸 수 있으며 위험 할 수 있습니다. 여기에서는 actor 객체의 toString () 속성을 덮어 씁니다.

const actor= {
    name: 'Harrison Ford',
    toString: 'Actor: Harrison Ford'
};

이제 fn isPlainObject ()를 정의하여 제공된 인수가 일반 객체인지 확인하고이 fn은 toString () 메소드를 사용하여이를 확인합니다.

function isPlainObject(value) {
    return value.toString() === '[object Object]';
}

isPlainObject(actor);        // TypeError : value.toString is not a function

// this is because inside actor object toString property is a string instead of inherited method from prototype

Map에는 키 이름에 대한 제한이 없으며 toString, constructor 등의 키 이름을 사용할 수 있습니다. 여기서 actorMap 객체에는 toString이라는 속성이 있지만 actorMap 객체의 프로토 타입에서 상속 된 toString () 메소드는 완벽하게 작동합니다.

const actorMap= new Map();

actorMap.set('name', 'Harrison Ford');

actorMap.set('toString', 'Actor: Harrison Ford');

function isMap(value) {
  return value.toString() === '[object Map]';
}

console.log(isMap(actorMap));     // true

사용자 입력으로 키가 생성되는 상황이 발생하면 일반 객체 대신 맵에서 해당 키를 가져와야합니다. 사용자가 toString, 생성자 등의 사용자 정의 필드 이름을 선택할 수 있기 때문에 일반 객체의 키 이름으로 인해 나중에이 객체를 사용하는 코드가 손상 될 수 있습니다. 따라서 올바른 솔루션은 사용자 인터페이스 상태를지도에 바인딩하는 것입니다.지도를 깰 수있는 방법은 없습니다.

const userCustomFieldsMap= new Map([['color', 'blue'], ['size', 'medium'], ['toString', 'A blue box']]);

3.지도는 반복 가능합니다.

일반 객체의 속성을 반복하려면 Object.entries () 또는 Object.keys ()가 필요합니다. Object.entries (plainObject)는 객체에서 추출 된 키 값 쌍의 배열을 반환합니다. 그런 다음 해당 키와 값을 재구성하고 일반 키와 값을 얻을 수 있습니다.

const colorHex= {
  'white': '#FFFFFF',
  'black': '#000000'
}

for(const [color, hex] of Object.entries(colorHex)) {
  console.log(color, hex);
}
//
'white' '#FFFFFF'   
'black' '#000000'

지도는 반복 가능하므로 Map을 반복하고 키를 구조화하기 위해 entry () 메소드가 필요하지 않으므로 각 요소가 쉼표로 구분 된 키 값 쌍의 배열로 존재하는 Map 내에서 값 배열을 Map에서 직접 수행 할 수 있습니다. .

const colorHexMap= new Map();
colorHexMap.set('white', '#FFFFFF');
colorHexMap.set('black', '#000000');


for(const [color, hex] of colorHexMap) {
  console.log(color, hex);
}
//'white' '#FFFFFF'   'black' '#000000'

또한 map.keys ()는 키에 대한 반복자를 반환하고 map.values ​​()는 값에 대한 반복자를 반환합니다.

4.지도의 크기를 쉽게 알 수 있습니다

일반 객체의 속성 수를 직접 결정할 수는 없습니다. Object.keys ()와 같은 도우미 fn이 필요합니다. Object.keys ()는 객체의 키로 배열을 반환하고 length 속성을 사용하여 키 수 또는 일반 객체의 크기를 얻을 수 있습니다.

const exams= {'John Rambo': '80%', 'James Bond': '60%'};

const sizeOfObj= Object.keys(exams).length;

console.log(sizeOfObj);       // 2

그러나지도의 경우 map.size 속성을 사용하여지도 크기에 직접 액세스 할 수 있습니다.

const examsMap= new Map([['John Rambo', '80%'], ['James Bond', '60%']]);

console.log(examsMap.size);

1

이 두 가지 팁은 맵을 사용할지 또는 오브젝트를 사용할지 결정하는 데 도움이 될 수 있습니다.

  • 런타임까지 키를 알 수없는 경우와 모든 키가 동일한 유형이고 모든 값이 같은 유형 인 경우 오브젝트에 대한 맵을 사용하십시오.

  • 객체가 각 키를 숫자 값, 부울 값 또는 기타 기본 값으로 문자열로 취급하므로 기본 값을 키로 저장해야하는 경우 맵을 사용하십시오.

  • 개별 요소에서 작동하는 논리가있는 경우 개체를 사용하십시오.

출처 : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Keyed_Collections#Object_and_Map_compared


2
이러한 팁은 이러한 기준에 따라 항목을 쉽게 분리 할 수없는 경향이 있으므로 특히 유용하지 않습니다. 키 / 값이 같은 유형일 때 맵이 유리한 이유는 무엇입니까? 클래스 / 구조체와 같은 객체, 컬렉션과 같은 맵을 사용하려고한다고 말하는 것처럼 들립니다. 두 번째는 요점을 잘 모르는 것으로 쓰여졌습니다. 문자열 등가 유형 ( "1"과 1)이 혼합되어 있거나 키 유형을 보존해야하는 경우에는 맵을 사용한다는 의미입니다. 마지막으로 나는 그것이 처음과 동일하다고 생각합니다. 어떤 물체가 무엇인지 모른다고 가정하면 모호합니다.
jgmjgm 2016 년

1

이것은 내가 기억하기에 짧은 길입니다 : KOI

  1. 열쇠. 객체 키는 문자열 또는 기호입니다. 맵 키는 숫자 (1과 "1"이 다름), 객체 NaN등일 수도 있습니다 . ===한 가지 예외를 제외하고 키를 구별하는 데 사용 NaN !== NaN하지만 NaN키로 사용할 수 있습니다 .
  2. 주문. 게재 신청서가 기억됩니다. 그래서 [...map]또는 [...map.keys()]특정 순서가 있습니다.
  3. 상호 작용. 개체 : obj[key]또는 obj.a(일부 언어, []그리고 []=정말 인터페이스의 일부). 지도가 get(), set(), has(), delete()당신이 사용할 수있는 등 참고 map[123]하지만, 일반 JS 객체로 사용된다.

0

모질라에 따르면

예제와 함께 짧은 시간 에 JavaScript 에서 객체 대 맵

객체-는지 도와 동일한 개념, 즉 데이터를 저장하기 위해 키-값 쌍을 사용합니다. 그러나 특정 상황에서지도의 성능을 향상시키는 약간의 차이가 있습니다.

Map- 은 데이터를 쌍 형태로 저장하는 데 도움이되는 데이터 구조입니다. 쌍은 고유 키와 키에 매핑 된 값으로 구성됩니다. 이중성을 방지하는 데 도움이됩니다.

주요 차이점

  • 지도는 객체의 인스턴스이지만 그 반대도 마찬가지입니다.

var map = new Map();
var obj = new Object(); 
console.log(obj instanceof Map);   // false
console.log(map instanceof Object);  // true

  • Object에서 키 필드의 데이터 유형은 정수, 문자열 및 기호로 제한됩니다. Map에서 키 필드는 모든 데이터 유형 (정수, 배열, 객체) 일 수 있습니다.

var map = new Map();//Empty 
map.set(1,'1');
map.set('one', 1);
map.set('{}', {name:'Hello world'});
map.set(12.3, 12.3)
map.set([12],[12345])

for(let [key,value] of map.entries())
  console.log(key+'---'+value)

  • 맵에서 요소의 원래 순서가 유지됩니다. 객체의 경우에는 그렇지 않습니다.

let obj ={
  1:'1',
  'one':1,
  '{}': {name:'Hello world'},
  12.3:12.3,
  [12]:[100]
}
console.log(obj)


당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.