core-js
Version:
Standard library
202 lines (194 loc) • 6.77 kB
JavaScript
// ECMAScript 6 collections shim
!function(){
var KEYS = COLLECTION_KEYS = safeSymbol('keys')
, VALUES = safeSymbol('values')
, STOREID = safeSymbol('storeId')
, WEAKDATA = safeSymbol('weakData')
, WEAKID = safeSymbol('weakId')
, SIZE = DESC ? safeSymbol('size') : 'size'
, uid = 0
, wid = 0;
function wrapSVZ(method, chain){
return function(a, b){
var result = method.call(this, same(a, -0) ? 0 : a, b);
return chain ? this : result;
}
}
function getCollection(C, NAME, methods, commonMethods, isMap, isWeak){
var ADDER_KEY = isMap ? 'set' : 'add'
, init = commonMethods.clear
, O = {};
function initFromIterable(that, iterable){
if(iterable != undefined)forOf(iterable, isMap, that[ADDER_KEY], that);
return that;
}
if(!(isNative(C) && (isWeak || has(C[PROTOTYPE], FOR_EACH)))){
// create collection constructor
C = function(iterable){
assertInstance(this, C, NAME);
isWeak ? hidden(this, WEAKID, wid++) : init.call(this);
initFromIterable(this, iterable);
}
set(C, SHIM, true);
assignHidden(assignHidden(C[PROTOTYPE], methods), commonMethods);
isWeak || defineProperty(C[PROTOTYPE], 'size', {get: function(){
return assertDefined(this[SIZE]);
}});
} else {
var Native = C
, proto = C[PROTOTYPE]
, inst = new C
, chain = inst[ADDER_KEY](isWeak ? {} : -0, 1)
, buggyZero;
// wrap to init collections from iterable
if(!(SYMBOL_ITERATOR in ArrayProto && C.length)){
C = function(iterable){
assertInstance(this, C, NAME);
return initFromIterable(new Native, iterable);
}
C[PROTOTYPE] = proto;
}
isWeak || inst[FOR_EACH](function(val, key){
if(same(key, -0))buggyZero = true;
});
if(framework){
// fix converting -0 key to +0
if(buggyZero){
hidden(proto, 'delete', wrapSVZ(proto['delete']));
hidden(proto, 'has', wrapSVZ(proto.has));
isMap && hidden(proto, 'get', wrapSVZ(proto.get));
}
// fix .add & .set for chaining
if(buggyZero || chain !== inst)hidden(proto, ADDER_KEY, wrapSVZ(proto[ADDER_KEY], true));
}
}
setToStringTag(C, NAME);
O[NAME] = C;
$define(GLOBAL + WRAP + FORCED * !isNative(C), O);
return C;
}
function fastKey(it, create){
// return it with 'S' prefix if it's string or with 'P' prefix for over primitives
if(!isObject(it))return (typeof it == 'string' ? 'S' : 'P') + it;
// if it hasn't object id - add next
if(!has(it, STOREID)){
if(create)hidden(it, STOREID, ++uid);
else return '';
}
// return object id with 'O' prefix
return 'O' + it[STOREID];
}
function collectionMethods($VALUES){
return {
// 23.1.3.1 Map.prototype.clear()
// 23.2.3.2 Set.prototype.clear()
clear: function(){
hidden(this, SIZE, 0);
hidden(this, KEYS, create(null));
if($VALUES == VALUES)hidden(this, VALUES, create(null));
},
// 23.1.3.3 Map.prototype.delete(key)
// 23.2.3.4 Set.prototype.delete(value)
'delete': function(key){
var index = fastKey(key)
, keys = this[KEYS]
, contains = index in keys;
if(contains){
delete keys[index];
if($VALUES == VALUES)delete this[VALUES][index];
this[SIZE]--;
}
return contains;
},
// 23.2.3.6 Set.prototype.forEach(callbackfn, thisArg = undefined)
// 23.1.3.5 Map.prototype.forEach(callbackfn, thisArg = undefined)
forEach: function(callbackfn, that /* = undefined */){
var f = ctx(callbackfn, that, 3)
, values = this[$VALUES]
, keys = this[KEYS]
, done = {}
, k, index;
do {
for(index in keys){
if(index in done)continue;
done[index] = true;
f(values[index], keys[index], this);
}
} while((k = getKeys(keys)).length && k[k.length - 1] != index);
},
// 23.1.3.7 Map.prototype.has(key)
// 23.2.3.7 Set.prototype.has(value)
has: function(key){
return fastKey(key) in this[KEYS];
}
}
}
// 23.1 Map Objects
Map = getCollection(Map, MAP, {
// 23.1.3.6 Map.prototype.get(key)
get: function(key){
return this[VALUES][fastKey(key)];
},
// 23.1.3.9 Map.prototype.set(key, value)
set: function(key, value){
var index = fastKey(key, true)
, values = this[VALUES];
if(!(index in values)){
this[KEYS][index] = same(key, -0) ? 0 : key;
this[SIZE]++;
}
values[index] = value;
return this;
}
}, collectionMethods(VALUES), true);
// 23.2 Set Objects
Set = getCollection(Set, SET, {
// 23.2.3.1 Set.prototype.add(value)
add: function(value){
var index = fastKey(value, true)
, values = this[KEYS];
if(!(index in values)){
values[index] = same(value, -0) ? 0 : value;
this[SIZE]++;
}
return this;
}
}, collectionMethods(KEYS));
function getWeakData(it){
has(it, WEAKDATA) || hidden(it, WEAKDATA, {});
return it[WEAKDATA];
}
function weakCollectionHas(key){
return isObject(key) && has(key, WEAKDATA) && has(key[WEAKDATA], this[WEAKID]);
}
var weakCollectionMethods = {
// 23.3.3.2 WeakMap.prototype.delete(key)
// 23.4.3.3 WeakSet.prototype.delete(value)
'delete': function(key){
return weakCollectionHas.call(this, key) && delete key[WEAKDATA][this[WEAKID]];
},
// 23.3.3.4 WeakMap.prototype.has(key)
// 23.4.3.4 WeakSet.prototype.has(value)
has: weakCollectionHas
};
// 23.3 WeakMap Objects
WeakMap = getCollection(WeakMap, WEAKMAP, {
// 23.3.3.3 WeakMap.prototype.get(key)
get: function(key){
if(isObject(key) && has(key, WEAKDATA))return key[WEAKDATA][this[WEAKID]];
},
// 23.3.3.5 WeakMap.prototype.set(key, value)
set: function(key, value){
getWeakData(assertObject(key))[this[WEAKID]] = value;
return this;
}
}, weakCollectionMethods, true, true);
// 23.4 WeakSet Objects
WeakSet = getCollection(WeakSet, WEAKSET, {
// 23.4.3.1 WeakSet.prototype.add(value)
add: function(value){
getWeakData(assertObject(value))[this[WEAKID]] = true;
return this;
}
}, weakCollectionMethods, false, true);
}();