You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
118 lines
5.0 KiB
118 lines
5.0 KiB
// ES2015 Symbol polyfill for environments that do not (or partially) support it |
|
|
|
'use strict'; |
|
|
|
var d = require('d') |
|
, validateSymbol = require('./validate-symbol') |
|
|
|
, create = Object.create, defineProperties = Object.defineProperties |
|
, defineProperty = Object.defineProperty, objPrototype = Object.prototype |
|
, NativeSymbol, SymbolPolyfill, HiddenSymbol, globalSymbols = create(null) |
|
, isNativeSafe; |
|
|
|
if (typeof Symbol === 'function') { |
|
NativeSymbol = Symbol; |
|
try { |
|
String(NativeSymbol()); |
|
isNativeSafe = true; |
|
} catch (ignore) {} |
|
} |
|
|
|
var generateName = (function () { |
|
var created = create(null); |
|
return function (desc) { |
|
var postfix = 0, name, ie11BugWorkaround; |
|
while (created[desc + (postfix || '')]) ++postfix; |
|
desc += (postfix || ''); |
|
created[desc] = true; |
|
name = '@@' + desc; |
|
defineProperty(objPrototype, name, d.gs(null, function (value) { |
|
// For IE11 issue see: |
|
// https://connect.microsoft.com/IE/feedbackdetail/view/1928508/ |
|
// ie11-broken-getters-on-dom-objects |
|
// https://github.com/medikoo/es6-symbol/issues/12 |
|
if (ie11BugWorkaround) return; |
|
ie11BugWorkaround = true; |
|
defineProperty(this, name, d(value)); |
|
ie11BugWorkaround = false; |
|
})); |
|
return name; |
|
}; |
|
}()); |
|
|
|
// Internal constructor (not one exposed) for creating Symbol instances. |
|
// This one is used to ensure that `someSymbol instanceof Symbol` always return false |
|
HiddenSymbol = function Symbol(description) { |
|
if (this instanceof HiddenSymbol) throw new TypeError('Symbol is not a constructor'); |
|
return SymbolPolyfill(description); |
|
}; |
|
|
|
// Exposed `Symbol` constructor |
|
// (returns instances of HiddenSymbol) |
|
module.exports = SymbolPolyfill = function Symbol(description) { |
|
var symbol; |
|
if (this instanceof Symbol) throw new TypeError('Symbol is not a constructor'); |
|
if (isNativeSafe) return NativeSymbol(description); |
|
symbol = create(HiddenSymbol.prototype); |
|
description = (description === undefined ? '' : String(description)); |
|
return defineProperties(symbol, { |
|
__description__: d('', description), |
|
__name__: d('', generateName(description)) |
|
}); |
|
}; |
|
defineProperties(SymbolPolyfill, { |
|
for: d(function (key) { |
|
if (globalSymbols[key]) return globalSymbols[key]; |
|
return (globalSymbols[key] = SymbolPolyfill(String(key))); |
|
}), |
|
keyFor: d(function (s) { |
|
var key; |
|
validateSymbol(s); |
|
for (key in globalSymbols) if (globalSymbols[key] === s) return key; |
|
}), |
|
|
|
// To ensure proper interoperability with other native functions (e.g. Array.from) |
|
// fallback to eventual native implementation of given symbol |
|
hasInstance: d('', (NativeSymbol && NativeSymbol.hasInstance) || SymbolPolyfill('hasInstance')), |
|
isConcatSpreadable: d('', (NativeSymbol && NativeSymbol.isConcatSpreadable) || |
|
SymbolPolyfill('isConcatSpreadable')), |
|
iterator: d('', (NativeSymbol && NativeSymbol.iterator) || SymbolPolyfill('iterator')), |
|
match: d('', (NativeSymbol && NativeSymbol.match) || SymbolPolyfill('match')), |
|
replace: d('', (NativeSymbol && NativeSymbol.replace) || SymbolPolyfill('replace')), |
|
search: d('', (NativeSymbol && NativeSymbol.search) || SymbolPolyfill('search')), |
|
species: d('', (NativeSymbol && NativeSymbol.species) || SymbolPolyfill('species')), |
|
split: d('', (NativeSymbol && NativeSymbol.split) || SymbolPolyfill('split')), |
|
toPrimitive: d('', (NativeSymbol && NativeSymbol.toPrimitive) || SymbolPolyfill('toPrimitive')), |
|
toStringTag: d('', (NativeSymbol && NativeSymbol.toStringTag) || SymbolPolyfill('toStringTag')), |
|
unscopables: d('', (NativeSymbol && NativeSymbol.unscopables) || SymbolPolyfill('unscopables')) |
|
}); |
|
|
|
// Internal tweaks for real symbol producer |
|
defineProperties(HiddenSymbol.prototype, { |
|
constructor: d(SymbolPolyfill), |
|
toString: d('', function () { return this.__name__; }) |
|
}); |
|
|
|
// Proper implementation of methods exposed on Symbol.prototype |
|
// They won't be accessible on produced symbol instances as they derive from HiddenSymbol.prototype |
|
defineProperties(SymbolPolyfill.prototype, { |
|
toString: d(function () { return 'Symbol (' + validateSymbol(this).__description__ + ')'; }), |
|
valueOf: d(function () { return validateSymbol(this); }) |
|
}); |
|
defineProperty(SymbolPolyfill.prototype, SymbolPolyfill.toPrimitive, d('', function () { |
|
var symbol = validateSymbol(this); |
|
if (typeof symbol === 'symbol') return symbol; |
|
return symbol.toString(); |
|
})); |
|
defineProperty(SymbolPolyfill.prototype, SymbolPolyfill.toStringTag, d('c', 'Symbol')); |
|
|
|
// Proper implementaton of toPrimitive and toStringTag for returned symbol instances |
|
defineProperty(HiddenSymbol.prototype, SymbolPolyfill.toStringTag, |
|
d('c', SymbolPolyfill.prototype[SymbolPolyfill.toStringTag])); |
|
|
|
// Note: It's important to define `toPrimitive` as last one, as some implementations |
|
// implement `toPrimitive` natively without implementing `toStringTag` (or other specified symbols) |
|
// And that may invoke error in definition flow: |
|
// See: https://github.com/medikoo/es6-symbol/issues/13#issuecomment-164146149 |
|
defineProperty(HiddenSymbol.prototype, SymbolPolyfill.toPrimitive, |
|
d('c', SymbolPolyfill.prototype[SymbolPolyfill.toPrimitive]));
|
|
|