├── .gitignore ├── .travis.yml ├── LICENSE.txt ├── README.md ├── bower.json ├── es6-collections.js ├── index.js ├── package.json └── test └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | components 2 | 3 | build 4 | 5 | node_modules -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 0.10 4 | - 0.12 -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (C) 2011 by Andrea Giammarchi, @WebReflection 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ES6 Harmony Collections [![build status](https://secure.travis-ci.org/WebReflection/es6-collections.png)](http://travis-ci.org/WebReflection/es6-collections) 2 | =========================================== 3 | 4 | 5 | [![browser support](https://ci.testling.com/WebReflection/es6-collections.png) 6 | ](https://ci.testling.com/WebReflection/es6-collections) 7 | 8 | 9 | # Deprecated 10 | 11 | This is a very old polyfill which served old browser decently for the last 4+ years. 12 | I am not actively maintaining this project but I would eventually accept PRs if you really need to use it. 13 | 14 | Please have a look at [better alternatives such ES-Shims](https://github.com/paulmillr/es6-shim) 15 | 16 | - - - 17 | 18 | 19 | 20 | The aim of this repository is to provide an **unobtrusive, performances oriented** shim for ES6 collections such **WeakMap**, **Map**, and **Set**. 21 | 22 | These global functions are already available in Firefox Nightly and Chrome Dev channel through *Enable Experimental JavaScript* in *chrome://flags/* section. 23 | 24 | 25 | Features 26 | -------- 27 | * compatible with **all browsers** and both **node.js** (`npm install es6-collections`) and **Rhino** 28 | * **100% unobtrusive** with any environment. If implemented in node V8 it exports native constructors rather than shims 29 | * **size and performances oriented** polyfill. It does not matter if the WeakMap is not perfect, it's just fast and not much more memory leaks prone than other shims. If you don't rely on magic, simply remember to `weakmap.delete(referedObject)` when *referedObject* is not needed anymore. 30 | * for browsers, it fits in **less than 1Kb** [once minzipped](https://github.com/WebReflection/es6-collections/blob/master/es6-collections.js) ... the smallest shim out there so far 31 | * 100% of code coverage 32 | * completely private shared behavior in order to easily maintain and update three collections at once 33 | 34 | 35 | New On Version 0.3.0 36 | -------------------- 37 | * API updated to the [MDN docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) 38 | * tests are replaced with mocha and testling, which gets testling table and better testing possibilities 39 | * polyfills for old browsers moved out to autopolyfiller 40 | * used prototypical shared methods approach since it demonstrates times better results - [test on jsperf](http://jsperf.com/object-create-method-vs-prototype-method) 41 | 42 | New On Version 0.2.0 43 | -------------------- 44 | * **removed** both **keys** and **values** properties since these are not in specs anymore 45 | * improved checks in order to do not fail with **NaN** or **-0** and **+0** as specified via specs 46 | * native Array.prototype.indexOf used when key/value to retrieve is not **NaN** or **-0** and **+0** (performances regardless checks) 47 | * updated **tests** including all methods behaviors plus `(Map|Set)#size()` test for **Mozilla only** 48 | 49 | 50 | The WeakMap Is Not Weak ... And Why 51 | ----------------------------------- 52 | * first of all, **ES6 Collections is not about WeakMap only** ... most likely is about **Map** ... anyway ... 53 | * **O(n)** against **O(1)** to link *keyObject* and value is a **no-go** for different reasons: 54 | * the random property attached to the object will be easily discoverable via *for/in* loop in all non ES5 capable engines, **obtrusive** 55 | * even in ES5 capable browser, to make the random property not discoverable we need to wrap native *Object.defineProperty*, *Object.defineProperties*, *Object.create*, *Object.getOwnPropertyNames*, plus eventually *Proxy*, which means the whole application will be **O(n) times slower for everything**, not Map or WeakMap only 56 | * there are **situations where a random property cannot be attached**, as example in Internet Explorer some object exposed in JavaScript may not accept runtime attached properties. The purpose of this shim is to be as cross platform as possible and **as safe as possible while others polyfills are able to break**, just as example, objects defined via [VB Classes](http://code.google.com/p/vbclass/) 57 | * it's simply not possible to create 100% WeakMap in ES5 only, the aim of this polyfill is to bring a 1:1 unobtrusive and reliable API rather than 1:1 implementation 58 | * if you think WeakMap, never existed until now, is the only thing you need, you may consider the first proposed alternative and simply walk away from this page 59 | * a polyfill aim is to bring a reliable API until the browser supports it and you can simply remove the polyfill dependency. The perfect implementation may be unnecessary, in this case obtrusive, or simply [YAGNI](http://en.wikipedia.org/wiki/You_ain't_gonna_need_it) 60 | 61 | 62 | Alternatives 63 | ------------ 64 | * the bigger and rich in dependencies [WeakMap shim from Mark S. Miller](http://code.google.com/p/es-lab/source/browse/trunk/src/ses/WeakMap.js), the best attempt to avoid undesired memory leaks. Bear in mind some leak is still possible plus *Object* natives are wrapped plus it brings WeakMap only 65 | * the unfortunately and so far slower and heavier, memory usage speaking, alternative from [Benvie Harmony Collections Shim](https://github.com/Benvie/ES6-Harmony-Collections-Shim) 66 | * differently implemented Map and Set (no WeakMap) from [Paul Millr](https://github.com/paulmillr/es6-shim), together with few others ES6 prototypes 67 | * another attempt based on valueOf to avoid IE enumerability, still problematic with *unknown* objects but less leaks prone from [Gozala](https://gist.github.com/1269991) 68 | 69 | 70 | Tests 71 | ----- 72 | Just type `$ mocha` or `$ npm test` from the project’s folder in terminal. 73 | 74 | 75 | Build 76 | ----- 77 | `$ npm build` to build browser version of bundle. 78 | 79 | 80 | License 81 | ------- 82 | 83 | *es6-collections* and the rest of the project is under Mit Style License 84 | 85 | Copyright (C) 2011 by Andrea Giammarchi, @WebReflection 86 | 87 | Permission is hereby granted, free of charge, to any person obtaining a copy 88 | of this software and associated documentation files (the "Software"), to deal 89 | in the Software without restriction, including without limitation the rights 90 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 91 | copies of the Software, and to permit persons to whom the Software is 92 | furnished to do so, subject to the following conditions: 93 | 94 | The above copyright notice and this permission notice shall be included in 95 | all copies or substantial portions of the Software. 96 | 97 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 98 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 99 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 100 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 101 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 102 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 103 | THE SOFTWARE. 104 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "es6-collections", 3 | "main": "index.js", 4 | "version": "0.5.5", 5 | "homepage": "https://github.com/WebReflection/es6-collections", 6 | "authors": [ 7 | "Deema Yvanow ", 8 | "Andrea Giammarchi (http://webreflection.blogspot.com/)" 9 | ], 10 | "description": "ES6 Harmony like collections such Map, WeakMap, and Set", 11 | "moduleType": [ 12 | "globals" 13 | ], 14 | "keywords": [ 15 | "WeakMap", 16 | "Map", 17 | "Set", 18 | "shim", 19 | "polyfill", 20 | "ES6", 21 | "Harmony" 22 | ], 23 | "license": "MIT", 24 | "ignore": [ 25 | "**/.*", 26 | "test" 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /es6-collections.js: -------------------------------------------------------------------------------- 1 | (function(e){function f(a,c){function b(a){if(!this||this.constructor!==b)return new b(a);this._keys=[];this._values=[];this._itp=[];this.objectOnly=c;a&&v.call(this,a)}c||w(a,"size",{get:x});a.constructor=b;b.prototype=a;return b}function v(a){this.add?a.forEach(this.add,this):a.forEach(function(a){this.set(a[0],a[1])},this)}function d(a){this.has(a)&&(this._keys.splice(b,1),this._values.splice(b,1),this._itp.forEach(function(a){b callback.call(context, key, value, mapObject) === not in specs` 42 | forEach: sharedForEach, 43 | // Map#clear(): 44 | clear: sharedClear 45 | }); 46 | } 47 | 48 | if (typeof Set == 'undefined' || typeof ((new Set).values) !== 'function' || !(new Set).values().next) { 49 | exports.Set = createCollection({ 50 | // Set#has(value:void*):boolean 51 | has: setHas, 52 | // Set#add(value:void*):boolean 53 | add: sharedAdd, 54 | // Set#delete(key:void*):boolean 55 | 'delete': sharedDelete, 56 | // Set#clear(): 57 | clear: sharedClear, 58 | // Set#keys(void):Iterator 59 | keys: sharedValues, // specs actually say "the same function object as the initial value of the values property" 60 | // Set#values(void):Iterator 61 | values: sharedValues, 62 | // Set#entries(void):Iterator 63 | entries: setEntries, 64 | // Set#forEach(callback:Function, context:void*):void ==> callback.call(context, value, index) === not in specs 65 | forEach: sharedForEach 66 | }); 67 | } 68 | 69 | if (typeof WeakSet == 'undefined') { 70 | exports.WeakSet = createCollection({ 71 | // WeakSet#delete(key:void*):boolean 72 | 'delete': sharedDelete, 73 | // WeakSet#add(value:void*):boolean 74 | add: sharedAdd, 75 | // WeakSet#clear(): 76 | clear: sharedClear, 77 | // WeakSet#has(value:void*):boolean 78 | has: setHas 79 | }, true); 80 | } 81 | 82 | 83 | /** 84 | * ES6 collection constructor 85 | * @return {Function} a collection class 86 | */ 87 | function createCollection(proto, objectOnly){ 88 | function Collection(a){ 89 | if (!this || this.constructor !== Collection) return new Collection(a); 90 | this._keys = []; 91 | this._values = []; 92 | this._itp = []; // iteration pointers 93 | this.objectOnly = objectOnly; 94 | 95 | //parse initial iterable argument passed 96 | if (a) init.call(this, a); 97 | } 98 | 99 | //define size for non object-only collections 100 | if (!objectOnly) { 101 | defineProperty(proto, 'size', { 102 | get: sharedSize 103 | }); 104 | } 105 | 106 | //set prototype 107 | proto.constructor = Collection; 108 | Collection.prototype = proto; 109 | 110 | return Collection; 111 | } 112 | 113 | 114 | /** parse initial iterable argument passed */ 115 | function init(a){ 116 | var i; 117 | //init Set argument, like `[1,2,3,{}]` 118 | if (this.add) 119 | a.forEach(this.add, this); 120 | //init Map argument like `[[1,2], [{}, 4]]` 121 | else 122 | a.forEach(function(a){this.set(a[0],a[1])}, this); 123 | } 124 | 125 | 126 | /** delete */ 127 | function sharedDelete(key) { 128 | if (this.has(key)) { 129 | this._keys.splice(i, 1); 130 | this._values.splice(i, 1); 131 | // update iteration pointers 132 | this._itp.forEach(function(p) { if (i < p[0]) p[0]--; }); 133 | } 134 | // Aurora here does it while Canary doesn't 135 | return -1 < i; 136 | }; 137 | 138 | function sharedGet(key) { 139 | return this.has(key) ? this._values[i] : undefined; 140 | } 141 | 142 | function has(list, key) { 143 | if (this.objectOnly && key !== Object(key)) 144 | throw new TypeError("Invalid value used as weak collection key"); 145 | //NaN or 0 passed 146 | if (key != key || key === 0) for (i = list.length; i-- && !is(list[i], key);){} 147 | else i = list.indexOf(key); 148 | return -1 < i; 149 | } 150 | 151 | function setHas(value) { 152 | return has.call(this, this._values, value); 153 | } 154 | 155 | function mapHas(value) { 156 | return has.call(this, this._keys, value); 157 | } 158 | 159 | /** @chainable */ 160 | function sharedSet(key, value) { 161 | this.has(key) ? 162 | this._values[i] = value 163 | : 164 | this._values[this._keys.push(key) - 1] = value 165 | ; 166 | return this; 167 | } 168 | 169 | /** @chainable */ 170 | function sharedAdd(value) { 171 | if (!this.has(value)) this._values.push(value); 172 | return this; 173 | } 174 | 175 | function sharedClear() { 176 | (this._keys || 0).length = 177 | this._values.length = 0; 178 | } 179 | 180 | /** keys, values, and iterate related methods */ 181 | function sharedKeys() { 182 | return sharedIterator(this._itp, this._keys); 183 | } 184 | 185 | function sharedValues() { 186 | return sharedIterator(this._itp, this._values); 187 | } 188 | 189 | function mapEntries() { 190 | return sharedIterator(this._itp, this._keys, this._values); 191 | } 192 | 193 | function setEntries() { 194 | return sharedIterator(this._itp, this._values, this._values); 195 | } 196 | 197 | function sharedIterator(itp, array, array2) { 198 | var p = [0], done = false; 199 | itp.push(p); 200 | return { 201 | next: function() { 202 | var v, k = p[0]; 203 | if (!done && k < array.length) { 204 | v = array2 ? [array[k], array2[k]]: array[k]; 205 | p[0]++; 206 | } else { 207 | done = true; 208 | itp.splice(itp.indexOf(p), 1); 209 | } 210 | return { done: done, value: v }; 211 | } 212 | }; 213 | } 214 | 215 | function sharedSize() { 216 | return this._values.length; 217 | } 218 | 219 | function sharedForEach(callback, context) { 220 | var it = this.entries(); 221 | for (;;) { 222 | var r = it.next(); 223 | if (r.done) break; 224 | callback.call(context, r.value[1], r.value[0], this); 225 | } 226 | } 227 | 228 | })(typeof exports != 'undefined' && typeof global != 'undefined' ? global : window ); 229 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "es6-collections", 3 | "version": "0.5.6", 4 | "description": "ES6 Harmony like collections such Map, WeakMap, and Set", 5 | "homepage": "https://github.com/WebReflection/es6-collections", 6 | "keywords": [ 7 | "WeakMap", 8 | "Map", 9 | "Set", 10 | "shim", 11 | "polyfill", 12 | "ES6", 13 | "Harmony" 14 | ], 15 | "author": "Andrea Giammarchi (http://webreflection.blogspot.com/)", 16 | "contributors": [ 17 | { 18 | "name": "Dmitry Ivanov", 19 | "email": "df.creative@gmail.com", 20 | "url": "http://github.com/dfcreative" 21 | } 22 | ], 23 | "repository": { 24 | "type": "git", 25 | "url": "git://github.com/WebReflection/es6-collections.git" 26 | }, 27 | "main": "./index.js", 28 | "engines": { 29 | "node": ">= 0.4.0" 30 | }, 31 | "dependencies": {}, 32 | "devDependencies": { 33 | "closurecompiler": "~1.3.2", 34 | "better-assert": "~1.0.1", 35 | "mocha": "~1.21.4", 36 | "browserify": "~5.12.0" 37 | }, 38 | "license": "MIT", 39 | "optionalDependencies": {}, 40 | "scripts": { 41 | "build": "npm run minify && npm run test", 42 | "minify": "./node_modules/closurecompiler/bin/ccjs index.js > es6-collections.js", 43 | "test": "./node_modules/mocha/bin/mocha" 44 | }, 45 | "testling": { 46 | "files": "test/*.js", 47 | "browsers": [ 48 | "ie/6..latest", 49 | "chrome/22..latest", 50 | "firefox/16..latest", 51 | "safari/latest", 52 | "opera/11.0..latest", 53 | "iphone/6", 54 | "ipad/6", 55 | "android-browser/latest" 56 | ], 57 | "harness": "mocha-bdd" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | // test the polyfill 2 | (this.window || global).Map = undefined; 3 | (this.window || global).Set = undefined; 4 | (this.window || global).WeakMap = undefined; 5 | (this.window || global).WeakSet = undefined; 6 | 7 | 8 | var assert = require('better-assert'); 9 | // require('../index'); 10 | require('../es6-collections'); 11 | 12 | describe('ES Collections test', function(){ 13 | it("WeakMap existence", function () { 14 | assert(WeakMap); 15 | }); 16 | 17 | it("WeakMap constructor behavior", function () { 18 | assert(new WeakMap instanceof WeakMap); 19 | assert(new WeakMap() instanceof WeakMap); 20 | var a = {}; 21 | var b = {}; 22 | var c = new WeakMap(); 23 | var m = new WeakMap([[a,1], [b,2], [c, 3]]); 24 | assert(m.has(a)); 25 | assert(m.has(b)); 26 | assert(m.has(c)); 27 | if ("__proto__" in {}) { 28 | assert((new WeakMap).__proto__.isPrototypeOf(new WeakMap())); 29 | assert((new WeakMap).__proto__ === WeakMap.prototype); 30 | } 31 | }); 32 | 33 | it("WeakMap#has", function () { 34 | var 35 | o = new WeakMap(), 36 | generic = {}, 37 | callback = function () {} 38 | ; 39 | assert(false === o.has(callback)); 40 | o.set(callback, generic); 41 | assert(true === o.has(callback)); 42 | }); 43 | 44 | it("WeakMap#get", function () { 45 | var 46 | o = new WeakMap(), 47 | generic = {}, 48 | callback = function () {} 49 | ; 50 | //:was assert(o.get(callback, 123) === 123); 51 | o.set(callback, generic); 52 | assert(o.get(callback, 123) === generic); 53 | assert(o.get(callback) === generic); 54 | }); 55 | 56 | it("WeakMap#set", function () { 57 | var 58 | o = new WeakMap(), 59 | generic = {}, 60 | callback = function () {} 61 | ; 62 | o.set(callback, generic); 63 | assert(o.get(callback) === generic); 64 | o.set(callback, callback); 65 | assert(o.get(callback) === callback); 66 | o.set(callback, o); 67 | assert(o.get(callback) === o); 68 | o.set(o, callback); 69 | assert(o.get(o) === callback); 70 | }); 71 | 72 | it("WeakMap#['delete']", function () { 73 | var 74 | o = new WeakMap(), 75 | generic = {}, 76 | callback = function () {} 77 | ; 78 | o.set(callback, generic); 79 | o.set(generic, callback); 80 | o.set(o, callback); 81 | assert(o.has(callback) && o.has(generic) && o.has(o)); 82 | o["delete"](callback); 83 | o["delete"](generic); 84 | o["delete"](o); 85 | assert(!o.has(callback) && !o.has(generic) && !o.has(o)); 86 | assert(o["delete"](o) === false); 87 | o.set(o, callback); 88 | assert(o["delete"](o)); 89 | }); 90 | 91 | it("non object key throws an error", function () { 92 | var o = new WeakMap(); 93 | try { 94 | o.set("key", o); 95 | assert(false); 96 | } catch(emAll) { 97 | assert(true); 98 | } 99 | }); 100 | 101 | it("Map existence", function () { 102 | assert(Map); 103 | }); 104 | 105 | it("Map constructor behavior", function () { 106 | assert(new Map instanceof Map); 107 | assert(new Map() instanceof Map); 108 | var a = 1; 109 | var b = {}; 110 | var c = new Map(); 111 | var m = new Map([[1,1], [b,2], [c, 3]]); 112 | assert(m.has(a)); 113 | assert(m.has(b)); 114 | assert(m.has(c)); 115 | assert(m.size, 3); 116 | if ("__proto__" in {}) { 117 | assert((new Map).__proto__.isPrototypeOf(new Map())); 118 | assert((new Map).__proto__ === Map.prototype); 119 | } 120 | }); 121 | 122 | it("Map#size - Mozilla only", function () { 123 | var 124 | o = new Map() 125 | ; 126 | if ("size" in o) { 127 | assert(o.size === 0); 128 | o.set("a", "a"); 129 | assert(o.size === 1); 130 | o["delete"]("a"); 131 | assert(o.size === 0); 132 | } 133 | }); 134 | 135 | it("Map#has", function () { 136 | var 137 | o = new Map(), 138 | generic = {}, 139 | callback = function () {} 140 | ; 141 | assert(false === o.has(callback)); 142 | o.set(callback, generic); 143 | assert(true === o.has(callback)); 144 | }); 145 | 146 | it("Map#get", function () { 147 | var 148 | o = new Map(), 149 | generic = {}, 150 | callback = function () {} 151 | ; 152 | //:was assert(o.get(callback, 123) === 123); 153 | o.set(callback, generic); 154 | assert(o.get(callback, 123) === generic); 155 | assert(o.get(callback) === generic); 156 | }); 157 | 158 | it("Map#set", function () { 159 | var 160 | o = new Map(), 161 | generic = {}, 162 | callback = function () {} 163 | ; 164 | o.set(callback, generic); 165 | assert(o.get(callback) === generic); 166 | o.set(callback, callback); 167 | assert(o.get(callback) === callback); 168 | o.set(callback, o); 169 | assert(o.get(callback) === o); 170 | o.set(o, callback); 171 | assert(o.get(o) === callback); 172 | o.set(NaN, generic); 173 | assert(o.has(NaN)); 174 | assert(o.get(NaN) === generic); 175 | o.set("key", undefined); 176 | assert(o.has("key")); 177 | assert(o.get("key") === undefined); 178 | 179 | assert(!o.has(-0)); 180 | assert(!o.has(0)); 181 | o.set(-0, callback); 182 | assert(o.has(-0)); 183 | assert(o.has(0)); 184 | assert(o.get(-0) === callback); 185 | assert(o.get(0) === callback); 186 | o.set(0, generic); 187 | assert(o.has(-0)); 188 | assert(o.has(0)); 189 | assert(o.get(-0) === generic); 190 | assert(o.get(0) === generic); 191 | }); 192 | 193 | it("Map#['delete']", function () { 194 | var 195 | o = new Map(), 196 | generic = {}, 197 | callback = function () {} 198 | ; 199 | o.set(callback, generic); 200 | o.set(generic, callback); 201 | o.set(o, callback); 202 | assert(o.has(callback) && o.has(generic) && o.has(o)); 203 | o["delete"](callback); 204 | o["delete"](generic); 205 | o["delete"](o); 206 | assert(!o.has(callback) && !o.has(generic) && !o.has(o)); 207 | assert(o["delete"](o) === false); 208 | o.set(o, callback); 209 | assert(o["delete"](o)); 210 | }); 211 | 212 | it("non object key does not throw an error", function () { 213 | var o = new Map(); 214 | try { 215 | o.set("key", o); 216 | assert(true); 217 | } catch(emAll) { 218 | assert(false); 219 | } 220 | }); 221 | 222 | it("keys, values, entries behavior", function () { 223 | // test that things get returned in insertion order as per the specs 224 | var o = new Map([["1", 1], ["2", 2], ["3", 3]]); 225 | var keys = o.keys(), values = o.values(); 226 | var k = keys.next(), v = values.next(); 227 | assert(k.value === "1" && v.value === 1); 228 | o.delete("2"); 229 | k = keys.next(), v = values.next(); 230 | assert(k.value === "3" && v.value === 3); 231 | // insertion of previously-removed item goes to the end 232 | o.set("2", 2); 233 | k = keys.next(), v = values.next(); 234 | assert(k.value === "2" && v.value === 2); 235 | // when called again, new iterator starts from beginning 236 | var entriesagain = o.entries(); 237 | assert(entriesagain.next().value[0] === "1"); 238 | assert(entriesagain.next().value[0] === "3"); 239 | assert(entriesagain.next().value[0] === "2"); 240 | // after a iterator is finished, don't return any more elements 241 | k = keys.next(), v = values.next(); 242 | assert(k.done && v.done); 243 | k = keys.next(), v = values.next(); 244 | assert(k.done && v.done); 245 | o.set("4", 4); 246 | k = keys.next(), v = values.next(); 247 | assert(k.done && v.done); 248 | // new element shows up in iterators that didn't yet finish 249 | assert(entriesagain.next().value[0] === "4"); 250 | assert(entriesagain.next().done); 251 | }); 252 | 253 | it("Map#forEach", function () { 254 | var o = new Map(), i; 255 | o.set("key 0", 0); 256 | o.set("key 1", 1); 257 | if ("forEach" in o) { 258 | o.forEach(function (value, key, obj) { 259 | assert(key === "key " + value); 260 | assert(obj === o); 261 | // even if dropped, keeps looping 262 | o["delete"](key); 263 | }); 264 | assert(!o.size); 265 | } 266 | }); 267 | 268 | it("Map#forEach with mutations", function () { 269 | var o = new Map([["0", 0], ["1", 1], ["2", 2]]), seen = []; 270 | o.forEach(function (value, key, obj) { 271 | seen.push(value); 272 | assert(obj === o); 273 | assert(""+value === key); 274 | // mutations work as expected 275 | if (value === 1) { 276 | o.delete("0"); // remove from before current index 277 | o.delete("2"); // remove from after current index 278 | o.set("3", 3); // insertion 279 | } else if (value === 3) { 280 | o.set("0", 0); // insertion at the end 281 | } 282 | }); 283 | assert(JSON.stringify(seen) === JSON.stringify([0, 1, 3, 0])); 284 | assert(JSON.stringify(o._values) === JSON.stringify([1, 3, 0])); 285 | }); 286 | 287 | it("Map#clear", function(){ 288 | var o = new Map(); 289 | o.set(1, '1'); 290 | o.set(2, '2'); 291 | o.set(3, '3'); 292 | o.clear(); 293 | assert(!o.size); 294 | }); 295 | 296 | it("Set existence", function () { 297 | assert(Set); 298 | }); 299 | 300 | it("Set constructor behavior", function () { 301 | assert(new Set instanceof Set); 302 | assert(new Set() instanceof Set); 303 | var s = new Set([1,2]); 304 | assert(s.has(1)); 305 | assert(s.has(2)); 306 | assert(s.size, 2); 307 | if ("__proto__" in {}) { 308 | assert((new Set).__proto__.isPrototypeOf(new Set())); 309 | assert((new Set).__proto__ === Set.prototype); 310 | } 311 | }); 312 | 313 | it("Set#size - Mozilla only", function () { 314 | var 315 | o = new Set() 316 | ; 317 | if ("size" in o) { 318 | assert(o.size === 0); 319 | o.add("a"); 320 | assert(o.size === 1); 321 | o["delete"]("a"); 322 | assert(o.size === 0); 323 | } 324 | }); 325 | 326 | it("Set#add", function () { 327 | var o = new Set(); 328 | assert(o.add(NaN)); 329 | assert(o.has(NaN)); 330 | }); 331 | 332 | it("Set#['delete']", function () { 333 | var 334 | o = new Set(), 335 | generic = {}, 336 | callback = function () {} 337 | ; 338 | o.add(callback); 339 | o.add(generic); 340 | o.add(o); 341 | assert(o.has(callback) && o.has(generic) && o.has(o)); 342 | o["delete"](callback); 343 | o["delete"](generic); 344 | o["delete"](o); 345 | assert(!o.has(callback) && !o.has(generic) && !o.has(o)); 346 | assert(o["delete"](o) === false); 347 | o.add(o); 348 | assert(o["delete"](o) === true); 349 | }); 350 | 351 | it("values behavior", function () { 352 | // test that things get returned in insertion order as per the specs 353 | var o = new Set([1, 2, 3]); 354 | assert(o.keys === o.values); // same function, as per the specs 355 | var values = o.values(); 356 | var v = values.next(); 357 | assert(v.value === 1); 358 | o.delete(2); 359 | v = values.next(); 360 | assert(v.value === 3); 361 | // insertion of previously-removed item goes to the end 362 | o.add(2); 363 | v = values.next(); 364 | assert(v.value === 2); 365 | // when called again, new iterator starts from beginning 366 | var entriesagain = o.entries(); 367 | assert(entriesagain.next().value[1] === 1); 368 | assert(entriesagain.next().value[1] === 3); 369 | assert(entriesagain.next().value[1] === 2); 370 | // after a iterator is finished, don't return any more elements 371 | v = values.next(); 372 | assert(v.done); 373 | v = values.next(); 374 | assert(v.done); 375 | o.add(4); 376 | v = values.next(); 377 | assert(v.done); 378 | // new element shows up in iterators that didn't yet finish 379 | assert(entriesagain.next().value[1] === 4); 380 | assert(entriesagain.next().done); 381 | }); 382 | 383 | it("Set#has", function () { 384 | var 385 | o = new Set(), 386 | generic = {}, 387 | callback = function () {} 388 | ; 389 | assert(false === o.has(callback)); 390 | o.add(callback); 391 | assert(true === o.has(callback)); 392 | }); 393 | 394 | it("Set#forEach", function () { 395 | var o = new Set(), i = 0; 396 | o.add("value 0"); 397 | o.add("value 1"); 398 | if ("forEach" in o) { 399 | o.forEach(function (value, sameValue, obj) { 400 | assert(value === "value " + i++); 401 | assert(obj === o); 402 | assert(value === sameValue); 403 | // even if dropped, keeps looping 404 | o["delete"](value); 405 | }); 406 | assert(!o.size); 407 | } 408 | }); 409 | 410 | it("Set#forEach with mutations", function () { 411 | var o = new Set([0, 1, 2]), seen = []; 412 | o.forEach(function (value, sameValue, obj) { 413 | seen.push(value); 414 | assert(obj === o); 415 | assert(value === sameValue); 416 | // mutations work as expected 417 | if (value === 1) { 418 | o.delete(0); // remove from before current index 419 | o.delete(2); // remove from after current index 420 | o.add(3); // insertion 421 | } else if (value === 3) { 422 | o.add(0); // insertion at the end 423 | } 424 | }); 425 | assert(JSON.stringify(seen) === JSON.stringify([0, 1, 3, 0])); 426 | assert(JSON.stringify(o._values) === JSON.stringify([1, 3, 0])); 427 | }); 428 | 429 | it("Set#clear", function(){ 430 | var o = new Set(); 431 | o.add(1); 432 | o.add(2); 433 | o.clear(); 434 | assert(!o.size); 435 | }); 436 | 437 | it("WeakSet existence", function () { 438 | assert(WeakSet); 439 | }); 440 | 441 | it("WeakSet constructor behavior", function () { 442 | assert(new WeakSet instanceof WeakSet); 443 | assert((new WeakSet) instanceof WeakSet); 444 | var a = {}, b = {}; 445 | var s = new WeakSet([a, b]); 446 | assert(s.has(a) && s.has(b)); 447 | if ("__proto__" in {}) { 448 | assert((new WeakSet).__proto__.isPrototypeOf(new WeakSet())); 449 | assert((new WeakSet).__proto__ === WeakSet.prototype); 450 | } 451 | }); 452 | 453 | it("Set#add, WeakSet#add, Map#set and WeakMap#set are chainable now", function(){ 454 | var s = new Set(); 455 | var ws = new WeakSet(); 456 | var m = new Map(); 457 | var wm = new WeakMap(); 458 | var a = {}, b = {}; 459 | 460 | s.add(1).add(2); 461 | assert(s.has(1) && s.has(2) && s.size === 2); 462 | 463 | ws.add(a).add(b); 464 | assert(ws.has(a) && ws.has(b)); 465 | 466 | m.set(1, 1).set(a, 2); 467 | assert(m.has(1) && m.has(a) && m.size === 2); 468 | 469 | wm.set(a, b).set(b, a); 470 | assert(wm.get(a) === b && wm.get(b) === a); 471 | }); 472 | 473 | it("Recognize any iterable as the constructor input", function(){ 474 | var a = new Set(new Set([1,2])); 475 | assert(a.has(1)); 476 | }); 477 | }); 478 | --------------------------------------------------------------------------------