polyfill-service
Version:
A polyfill combinator
106 lines (93 loc) • 3.13 kB
JavaScript
(function(global) {
// Deleted map items mess with iterator pointers, so rather than removing them mark them as deleted. Can't use undefined or null since those both valid keys so use a private symbol.
var undefMarker = Symbol('undef');
// NaN cannot be found in an array using indexOf, so we encode NaNs using a private symbol.
var NaNMarker = Symbol('NaN');
function encodeVal(data) {
return Number.isNaN(data) ? NaNMarker : data;
}
function decodeVal(encodedData) {
return (encodedData === NaNMarker) ? NaN : encodedData;
}
function makeIterator(setInst, getter) {
var nextIdx = 0;
return {
next: function() {
while (setInst._values[nextIdx] === undefMarker) nextIdx++;
if (nextIdx === setInst._values.length) {
return {value: void 0, done: true};
}
else {
return {value: getter.call(setInst, nextIdx++), done: false};
}
}
};
}
var Set = function Set() {
var data = arguments[0];
this._values = [];
this.size = this._size = 0;
// If `data` is iterable (indicated by presence of a forEach method), pre-populate the set
data && (typeof data.forEach === 'function') && data.forEach(function (item) {
this.add.call(this, item);
}, this);
};
// Some old engines do not support ES5 getters/setters. Since Set only requires these for the size property, we can fall back to setting the size property statically each time the size of the set changes.
try {
Object.defineProperty(Set.prototype, 'size', {
get: function() {
return this._size;
}
});
} catch(e) {
}
Set.prototype['add'] = function(value) {
value = encodeVal(value);
if (this._values.indexOf(value) === -1) {
this._values.push(value);
this.size = ++this._size;
}
return this;
};
Set.prototype['has'] = function(value) {
return (this._values.indexOf(encodeVal(value)) !== -1);
};
Set.prototype['delete'] = function(value) {
var idx = this._values.indexOf(encodeVal(value));
if (idx === -1) return false;
this._values[idx] = undefMarker;
this.size = --this._size;
return true;
};
Set.prototype['clear'] = function() {
this._values = [];
this.size = this._size = 0;
};
Set.prototype[Symbol.iterator] =
Set.prototype['values'] =
Set.prototype['keys'] = function() {
var iterator = makeIterator(this, function(i) { return decodeVal(this._values[i]); });
iterator[Symbol.iterator] = this.keys.bind(this);
return iterator;
};
Set.prototype['entries'] = function() {
var iterator = makeIterator(this, function(i) { return [decodeVal(this._values[i]), decodeVal(this._values[i])]; });
iterator[Symbol.iterator] = this.entries.bind(this);
return iterator;
};
Set.prototype['forEach'] = function(callbackFn, thisArg) {
thisArg = thisArg || global;
var iterator = this.entries();
var result = iterator.next();
while (result.done === false) {
callbackFn.call(thisArg, result.value[1], result.value[0], this);
result = iterator.next();
}
};
Set.prototype['constructor'] =
Set.prototype[Symbol.species] = Set;
Set.prototype.constructor = Set;
Set.name = "Set";
// Export the object
global.Set = Set;
}(this));