$.collection-strong.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. 'use strict';
  2. var $ = require('./$')
  3. , hide = require('./$.hide')
  4. , redefineAll = require('./$.redefine-all')
  5. , ctx = require('./$.ctx')
  6. , strictNew = require('./$.strict-new')
  7. , defined = require('./$.defined')
  8. , forOf = require('./$.for-of')
  9. , $iterDefine = require('./$.iter-define')
  10. , step = require('./$.iter-step')
  11. , ID = require('./$.uid')('id')
  12. , $has = require('./$.has')
  13. , isObject = require('./$.is-object')
  14. , setSpecies = require('./$.set-species')
  15. , DESCRIPTORS = require('./$.descriptors')
  16. , isExtensible = Object.isExtensible || isObject
  17. , SIZE = DESCRIPTORS ? '_s' : 'size'
  18. , id = 0;
  19. var fastKey = function(it, create){
  20. // return primitive with prefix
  21. if(!isObject(it))return typeof it == 'symbol' ? it : (typeof it == 'string' ? 'S' : 'P') + it;
  22. if(!$has(it, ID)){
  23. // can't set id to frozen object
  24. if(!isExtensible(it))return 'F';
  25. // not necessary to add id
  26. if(!create)return 'E';
  27. // add missing object id
  28. hide(it, ID, ++id);
  29. // return object id with prefix
  30. } return 'O' + it[ID];
  31. };
  32. var getEntry = function(that, key){
  33. // fast case
  34. var index = fastKey(key), entry;
  35. if(index !== 'F')return that._i[index];
  36. // frozen object case
  37. for(entry = that._f; entry; entry = entry.n){
  38. if(entry.k == key)return entry;
  39. }
  40. };
  41. module.exports = {
  42. getConstructor: function(wrapper, NAME, IS_MAP, ADDER){
  43. var C = wrapper(function(that, iterable){
  44. strictNew(that, C, NAME);
  45. that._i = $.create(null); // index
  46. that._f = undefined; // first entry
  47. that._l = undefined; // last entry
  48. that[SIZE] = 0; // size
  49. if(iterable != undefined)forOf(iterable, IS_MAP, that[ADDER], that);
  50. });
  51. redefineAll(C.prototype, {
  52. // 23.1.3.1 Map.prototype.clear()
  53. // 23.2.3.2 Set.prototype.clear()
  54. clear: function clear(){
  55. for(var that = this, data = that._i, entry = that._f; entry; entry = entry.n){
  56. entry.r = true;
  57. if(entry.p)entry.p = entry.p.n = undefined;
  58. delete data[entry.i];
  59. }
  60. that._f = that._l = undefined;
  61. that[SIZE] = 0;
  62. },
  63. // 23.1.3.3 Map.prototype.delete(key)
  64. // 23.2.3.4 Set.prototype.delete(value)
  65. 'delete': function(key){
  66. var that = this
  67. , entry = getEntry(that, key);
  68. if(entry){
  69. var next = entry.n
  70. , prev = entry.p;
  71. delete that._i[entry.i];
  72. entry.r = true;
  73. if(prev)prev.n = next;
  74. if(next)next.p = prev;
  75. if(that._f == entry)that._f = next;
  76. if(that._l == entry)that._l = prev;
  77. that[SIZE]--;
  78. } return !!entry;
  79. },
  80. // 23.2.3.6 Set.prototype.forEach(callbackfn, thisArg = undefined)
  81. // 23.1.3.5 Map.prototype.forEach(callbackfn, thisArg = undefined)
  82. forEach: function forEach(callbackfn /*, that = undefined */){
  83. var f = ctx(callbackfn, arguments.length > 1 ? arguments[1] : undefined, 3)
  84. , entry;
  85. while(entry = entry ? entry.n : this._f){
  86. f(entry.v, entry.k, this);
  87. // revert to the last existing entry
  88. while(entry && entry.r)entry = entry.p;
  89. }
  90. },
  91. // 23.1.3.7 Map.prototype.has(key)
  92. // 23.2.3.7 Set.prototype.has(value)
  93. has: function has(key){
  94. return !!getEntry(this, key);
  95. }
  96. });
  97. if(DESCRIPTORS)$.setDesc(C.prototype, 'size', {
  98. get: function(){
  99. return defined(this[SIZE]);
  100. }
  101. });
  102. return C;
  103. },
  104. def: function(that, key, value){
  105. var entry = getEntry(that, key)
  106. , prev, index;
  107. // change existing entry
  108. if(entry){
  109. entry.v = value;
  110. // create new entry
  111. } else {
  112. that._l = entry = {
  113. i: index = fastKey(key, true), // <- index
  114. k: key, // <- key
  115. v: value, // <- value
  116. p: prev = that._l, // <- previous entry
  117. n: undefined, // <- next entry
  118. r: false // <- removed
  119. };
  120. if(!that._f)that._f = entry;
  121. if(prev)prev.n = entry;
  122. that[SIZE]++;
  123. // add to index
  124. if(index !== 'F')that._i[index] = entry;
  125. } return that;
  126. },
  127. getEntry: getEntry,
  128. setStrong: function(C, NAME, IS_MAP){
  129. // add .keys, .values, .entries, [@@iterator]
  130. // 23.1.3.4, 23.1.3.8, 23.1.3.11, 23.1.3.12, 23.2.3.5, 23.2.3.8, 23.2.3.10, 23.2.3.11
  131. $iterDefine(C, NAME, function(iterated, kind){
  132. this._t = iterated; // target
  133. this._k = kind; // kind
  134. this._l = undefined; // previous
  135. }, function(){
  136. var that = this
  137. , kind = that._k
  138. , entry = that._l;
  139. // revert to the last existing entry
  140. while(entry && entry.r)entry = entry.p;
  141. // get next entry
  142. if(!that._t || !(that._l = entry = entry ? entry.n : that._t._f)){
  143. // or finish the iteration
  144. that._t = undefined;
  145. return step(1);
  146. }
  147. // return step by kind
  148. if(kind == 'keys' )return step(0, entry.k);
  149. if(kind == 'values')return step(0, entry.v);
  150. return step(0, [entry.k, entry.v]);
  151. }, IS_MAP ? 'entries' : 'values' , !IS_MAP, true);
  152. // add [@@species], 23.1.2.2, 23.2.2.2
  153. setSpecies(NAME);
  154. }
  155. };