Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit fdba297

Browse files
committed
refactor(HashMap): use ES6Map under the hood
1 parent 4db3ac1 commit fdba297

File tree

9 files changed

+67
-97
lines changed

9 files changed

+67
-97
lines changed

src/Angular.js

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
hasOwnProperty,
9090
createMap,
9191
stringify,
92+
ES6Map,
9293
9394
NODE_TYPE_ELEMENT,
9495
NODE_TYPE_ATTRIBUTE,
@@ -752,6 +753,7 @@ function arrayRemove(array, value) {
752753
// A minimal ES6 Map implementation.
753754
// Should be bug/feature equivelent to the native implementations of supported browsers.
754755
// See https://kangax.github.io/compat-table/es6/#test-Map
756+
var nanPlaceholder = Object.create(null);
755757
function ES6MapShim() {
756758
this._keys = [];
757759
this._values = [];
@@ -765,23 +767,37 @@ ES6MapShim.prototype = {
765767
}
766768
return (this._lastIndex = (this._keys.indexOf(this._lastKey = key)));
767769
},
770+
_transformKey: function(key) {
771+
return isNumberNaN(key) ? nanPlaceholder : key;
772+
},
768773
get: function(key) {
769-
var idx = this._idx(key);
774+
var idx = this._idx(this._transformKey(key));
770775
if (idx !== -1) {
771776
return this._values[idx];
772777
}
773778
},
774779
set: function(key, value) {
780+
key = this._transformKey(key);
775781
var idx = this._idx(key);
776782
if (idx === -1) {
777783
idx = this._lastIndex = this._keys.length;
778-
this._lastKey = key;
779784
}
780785
this._keys[idx] = key;
781786
this._values[idx] = value;
782787

783788
// Support: IE11
784789
// Do not `return this` to simulate the partial IE11 implementation
790+
},
791+
delete: function(key) {
792+
var idx = this._idx(this._transformKey(key));
793+
if (idx === -1) {
794+
return false;
795+
}
796+
this._keys.splice(idx, 1);
797+
this._values.splice(idx, 1);
798+
this._lastKey = NaN;
799+
this._lastIndex = -1;
800+
return true;
785801
}
786802
};
787803

src/apis.js

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
'use strict';
22

3+
/* global
4+
ES6Map */
35

46
/**
57
* Computes a hash of an 'obj'.
@@ -36,13 +38,8 @@ function hashKey(obj, nextUidFn) {
3638
/**
3739
* HashMap which can use objects as keys
3840
*/
39-
function HashMap(array, isolatedUid) {
40-
if (isolatedUid) {
41-
var uid = 0;
42-
this.nextUid = function() {
43-
return ++uid;
44-
};
45-
}
41+
function HashMap(array) {
42+
this._map = new ES6Map();
4643
forEach(array, this.put, this);
4744
}
4845
HashMap.prototype = {
@@ -52,24 +49,24 @@ HashMap.prototype = {
5249
* @param value value to store can be any type
5350
*/
5451
put: function(key, value) {
55-
this[hashKey(key, this.nextUid)] = value;
52+
this._map.set(key, value);
5653
},
5754

5855
/**
5956
* @param key
6057
* @returns {Object} the value for the key
6158
*/
6259
get: function(key) {
63-
return this[hashKey(key, this.nextUid)];
60+
return this._map.get(key);
6461
},
6562

6663
/**
6764
* Remove the key/value pair
6865
* @param key
6966
*/
7067
remove: function(key) {
71-
var value = this[key = hashKey(key, this.nextUid)];
72-
delete this[key];
68+
var value = this._map.get(key);
69+
this._map.delete(key);
7370
return value;
7471
}
7572
};

src/auto/injector.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -649,7 +649,7 @@ function createInjector(modulesToLoad, strictDi) {
649649
var INSTANTIATING = {},
650650
providerSuffix = 'Provider',
651651
path = [],
652-
loadedModules = new HashMap([], true),
652+
loadedModules = new HashMap(),
653653
providerCache = {
654654
$provide: {
655655
provider: supportObject(provider),

src/ngMock/angular-mocks.js

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2941,12 +2941,6 @@ angular.mock.$RootScopeDecorator = ['$delegate', function($delegate) {
29412941
delete fn.$inject;
29422942
});
29432943

2944-
angular.forEach(currentSpec.$modules, function(module) {
2945-
if (module && module.$$hashKey) {
2946-
module.$$hashKey = undefined;
2947-
}
2948-
});
2949-
29502944
currentSpec.$injector = null;
29512945
currentSpec.$modules = null;
29522946
currentSpec.$providerInjector = null;

test/ApiSpecs.js

Lines changed: 21 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -5,52 +5,35 @@ describe('api', function() {
55
describe('HashMap', function() {
66
it('should do basic crud', function() {
77
var map = new HashMap();
8-
var key = {};
9-
var value1 = {};
10-
var value2 = {};
11-
map.put(key, value1);
12-
map.put(key, value2);
13-
expect(map.get(key)).toBe(value2);
14-
expect(map.get({})).toBeUndefined();
15-
expect(map.remove(key)).toBe(value2);
16-
expect(map.get(key)).toBeUndefined();
8+
var keys = [{}, {}, {}];
9+
var values = [{}, {}, {}];
10+
11+
map.put(keys[0], values[1]);
12+
map.put(keys[0], values[0]);
13+
expect(map.get(keys[0])).toBe(values[0]);
14+
expect(map.get(keys[1])).toBeUndefined();
15+
16+
map.put(keys[1], values[1]);
17+
map.put(keys[2], values[2]);
18+
expect(map.remove(keys[0])).toBe(values[0]);
19+
expect(map.remove(keys[0])).toBeUndefined();
20+
21+
expect(map.get(keys[1])).toBe(values[1]);
22+
expect(map.get(keys[2])).toBe(values[2]);
1723
});
1824

1925
it('should init from an array', function() {
20-
var map = new HashMap(['a','b']);
26+
var map = new HashMap(['a', 'b']);
2127
expect(map.get('a')).toBe(0);
2228
expect(map.get('b')).toBe(1);
2329
expect(map.get('c')).toBeUndefined();
2430
});
2531

26-
it('should maintain hashKey for object keys', function() {
27-
var map = new HashMap();
28-
var key = {};
29-
map.get(key);
30-
expect(key.$$hashKey).toBeDefined();
31-
});
32-
33-
it('should maintain hashKey for function keys', function() {
34-
var map = new HashMap();
35-
var key = function() {};
36-
map.get(key);
37-
expect(key.$$hashKey).toBeDefined();
38-
});
39-
40-
it('should share hashKey between HashMap by default', function() {
41-
var map1 = new HashMap(), map2 = new HashMap();
42-
var key1 = {}, key2 = {};
43-
map1.get(key1);
44-
map2.get(key2);
45-
expect(key1.$$hashKey).not.toEqual(key2.$$hashKey);
46-
});
47-
48-
it('should maintain hashKey per HashMap if flag is passed', function() {
49-
var map1 = new HashMap([], true), map2 = new HashMap([], true);
50-
var key1 = {}, key2 = {};
51-
map1.get(key1);
52-
map2.get(key2);
53-
expect(key1.$$hashKey).toEqual(key2.$$hashKey);
32+
it('should init from an object', function() {
33+
var map = new HashMap({one: 'a', two: 'b'});
34+
expect(map.get('a')).toBe('one');
35+
expect(map.get('b')).toBe('two');
36+
expect(map.get('c')).toBeUndefined();
5437
});
5538
});
5639
});

test/helpers/testabilityPatch.js

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -54,17 +54,14 @@ beforeEach(function() {
5454
afterEach(function() {
5555
var count, cache;
5656

57-
// both of these nodes are persisted across tests
58-
// and therefore the hashCode may be cached
59-
var node = window.document.querySelector('html');
60-
if (node) {
61-
node.$$hashKey = null;
62-
}
63-
var bod = window.document.body;
64-
if (bod) {
65-
bod.$$hashKey = null;
66-
}
67-
window.document.$$hashKey = null;
57+
// These Nodes are persisted across tests.
58+
// They used to be assigned a `$$hashKey` when animated, but not any more.
59+
var doc = window.document;
60+
var html = doc.querySelector('html');
61+
var body = doc.body;
62+
expect(doc.$$hashKey).toBeFalsy();
63+
expect(html && html.$$hashKey).toBeFalsy();
64+
expect(body && body.$$hashKey).toBeFalsy();
6865

6966
if (this.$injector) {
7067
var $rootScope = this.$injector.get('$rootScope');

test/ng/directive/selectSpec.js

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1515,8 +1515,8 @@ describe('select', function() {
15151515
'number:1',
15161516
'boolean:true',
15171517
'object:null',
1518+
'object:3',
15181519
'object:4',
1519-
'object:5',
15201520
'number:NaN'
15211521
);
15221522

@@ -1541,15 +1541,15 @@ describe('select', function() {
15411541
browserTrigger(element, 'change');
15421542

15431543
var arrayVal = ['a'];
1544-
arrayVal.$$hashKey = 'object:5';
1544+
arrayVal.$$hashKey = 'object:4';
15451545

15461546
expect(scope.selected).toEqual([
15471547
'string',
15481548
undefined,
15491549
1,
15501550
true,
15511551
null,
1552-
{prop: 'value', $$hashKey: 'object:4'},
1552+
{prop: 'value', $$hashKey: 'object:3'},
15531553
arrayVal,
15541554
NaN
15551555
]);
@@ -1862,10 +1862,10 @@ describe('select', function() {
18621862
scope.$digest();
18631863

18641864
optionElements = element.find('option');
1865-
expect(element.val()).toBe(prop === 'ngValue' ? 'object:4' : 'C');
1865+
expect(element.val()).toBe(prop === 'ngValue' ? 'object:3' : 'C');
18661866
expect(optionElements.length).toEqual(3);
18671867
expect(optionElements[2].selected).toBe(true);
1868-
expect(scope.obj.value).toEqual(prop === 'ngValue' ? {name: 'C', $$hashKey: 'object:4'} : 'C');
1868+
expect(scope.obj.value).toEqual(prop === 'ngValue' ? {name: 'C', $$hashKey: 'object:3'} : 'C');
18691869
});
18701870

18711871

@@ -2174,9 +2174,9 @@ describe('select', function() {
21742174
expect(optionElements.length).toEqual(4);
21752175
expect(scope.obj.value).toEqual(prop === 'ngValue' ?
21762176
[
2177-
{name: 'A', $$hashKey: 'object:4', disabled: true},
2178-
{name: 'C', $$hashKey: 'object:6'},
2179-
{name: 'D', $$hashKey: 'object:7', disabled: true}
2177+
{name: 'A', $$hashKey: 'object:3', disabled: true},
2178+
{name: 'C', $$hashKey: 'object:5'},
2179+
{name: 'D', $$hashKey: 'object:6', disabled: true}
21802180
] :
21812181
['A', 'C', 'D']
21822182
);
@@ -2228,13 +2228,13 @@ describe('select', function() {
22282228
scope.$digest();
22292229

22302230
optionElements = element.find('option');
2231-
expect(element.val()).toEqual(prop === 'ngValue' ? ['object:4', 'object:5'] : ['B', 'C']);
2231+
expect(element.val()).toEqual(prop === 'ngValue' ? ['object:4', 'object:7'] : ['B', 'C']);
22322232
expect(optionElements.length).toEqual(3);
22332233
expect(optionElements[1].selected).toBe(true);
22342234
expect(optionElements[2].selected).toBe(true);
22352235
expect(scope.obj.value).toEqual(prop === 'ngValue' ?
22362236
[{ name: 'B', $$hashKey: 'object:4'},
2237-
{name: 'C', $$hashKey: 'object:5'}] :
2237+
{name: 'C', $$hashKey: 'object:7'}] :
22382238
['B', 'C']);
22392239
});
22402240

test/ngAnimate/animateSpec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1385,7 +1385,7 @@ describe('animations', function() {
13851385
expect(capturedAnimation[1]).toBe('leave');
13861386

13871387
// $$hashKey causes comparison issues
1388-
expect(element.parent()[0]).toEqual(parent[0]);
1388+
expect(element.parent()[0]).toBe(parent[0]);
13891389

13901390
options = capturedAnimation[2];
13911391
expect(options.addClass).toEqual('pink');

test/ngMock/angular-mocksSpec.js

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -795,23 +795,6 @@ describe('ngMock', function() {
795795
});
796796
});
797797

798-
describe('module cleanup', function() {
799-
function testFn() {
800-
801-
}
802-
803-
it('should add hashKey to module function', function() {
804-
module(testFn);
805-
inject(function() {
806-
expect(testFn.$$hashKey).toBeDefined();
807-
});
808-
});
809-
810-
it('should cleanup hashKey after previous test', function() {
811-
expect(testFn.$$hashKey).toBeUndefined();
812-
});
813-
});
814-
815798
describe('$inject cleanup', function() {
816799
function testFn() {
817800

0 commit comments

Comments
 (0)