123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. // ES2015 Symbol polyfill for environments that do not (or partially) support it
  2. 'use strict';
  3. var d = require('d')
  4. , validateSymbol = require('./validate-symbol')
  5. , create = Object.create, defineProperties = Object.defineProperties
  6. , defineProperty = Object.defineProperty, objPrototype = Object.prototype
  7. , NativeSymbol, SymbolPolyfill, HiddenSymbol, globalSymbols = create(null)
  8. , isNativeSafe;
  9. if (typeof Symbol === 'function') {
  10. NativeSymbol = Symbol;
  11. try {
  12. String(NativeSymbol());
  13. isNativeSafe = true;
  14. } catch (ignore) {}
  15. }
  16. var generateName = (function () {
  17. var created = create(null);
  18. return function (desc) {
  19. var postfix = 0, name, ie11BugWorkaround;
  20. while (created[desc + (postfix || '')]) ++postfix;
  21. desc += (postfix || '');
  22. created[desc] = true;
  23. name = '@@' + desc;
  24. defineProperty(objPrototype, name, d.gs(null, function (value) {
  25. // For IE11 issue see:
  26. // https://connect.microsoft.com/IE/feedbackdetail/view/1928508/
  27. // ie11-broken-getters-on-dom-objects
  28. // https://github.com/medikoo/es6-symbol/issues/12
  29. if (ie11BugWorkaround) return;
  30. ie11BugWorkaround = true;
  31. defineProperty(this, name, d(value));
  32. ie11BugWorkaround = false;
  33. }));
  34. return name;
  35. };
  36. }());
  37. // Internal constructor (not one exposed) for creating Symbol instances.
  38. // This one is used to ensure that `someSymbol instanceof Symbol` always return false
  39. HiddenSymbol = function Symbol(description) {
  40. if (this instanceof HiddenSymbol) throw new TypeError('Symbol is not a constructor');
  41. return SymbolPolyfill(description);
  42. };
  43. // Exposed `Symbol` constructor
  44. // (returns instances of HiddenSymbol)
  45. module.exports = SymbolPolyfill = function Symbol(description) {
  46. var symbol;
  47. if (this instanceof Symbol) throw new TypeError('Symbol is not a constructor');
  48. if (isNativeSafe) return NativeSymbol(description);
  49. symbol = create(HiddenSymbol.prototype);
  50. description = (description === undefined ? '' : String(description));
  51. return defineProperties(symbol, {
  52. __description__: d('', description),
  53. __name__: d('', generateName(description))
  54. });
  55. };
  56. defineProperties(SymbolPolyfill, {
  57. for: d(function (key) {
  58. if (globalSymbols[key]) return globalSymbols[key];
  59. return (globalSymbols[key] = SymbolPolyfill(String(key)));
  60. }),
  61. keyFor: d(function (s) {
  62. var key;
  63. validateSymbol(s);
  64. for (key in globalSymbols) if (globalSymbols[key] === s) return key;
  65. }),
  66. // To ensure proper interoperability with other native functions (e.g. Array.from)
  67. // fallback to eventual native implementation of given symbol
  68. hasInstance: d('', (NativeSymbol && NativeSymbol.hasInstance) || SymbolPolyfill('hasInstance')),
  69. isConcatSpreadable: d('', (NativeSymbol && NativeSymbol.isConcatSpreadable) ||
  70. SymbolPolyfill('isConcatSpreadable')),
  71. iterator: d('', (NativeSymbol && NativeSymbol.iterator) || SymbolPolyfill('iterator')),
  72. match: d('', (NativeSymbol && NativeSymbol.match) || SymbolPolyfill('match')),
  73. replace: d('', (NativeSymbol && NativeSymbol.replace) || SymbolPolyfill('replace')),
  74. search: d('', (NativeSymbol && NativeSymbol.search) || SymbolPolyfill('search')),
  75. species: d('', (NativeSymbol && NativeSymbol.species) || SymbolPolyfill('species')),
  76. split: d('', (NativeSymbol && NativeSymbol.split) || SymbolPolyfill('split')),
  77. toPrimitive: d('', (NativeSymbol && NativeSymbol.toPrimitive) || SymbolPolyfill('toPrimitive')),
  78. toStringTag: d('', (NativeSymbol && NativeSymbol.toStringTag) || SymbolPolyfill('toStringTag')),
  79. unscopables: d('', (NativeSymbol && NativeSymbol.unscopables) || SymbolPolyfill('unscopables'))
  80. });
  81. // Internal tweaks for real symbol producer
  82. defineProperties(HiddenSymbol.prototype, {
  83. constructor: d(SymbolPolyfill),
  84. toString: d('', function () { return this.__name__; })
  85. });
  86. // Proper implementation of methods exposed on Symbol.prototype
  87. // They won't be accessible on produced symbol instances as they derive from HiddenSymbol.prototype
  88. defineProperties(SymbolPolyfill.prototype, {
  89. toString: d(function () { return 'Symbol (' + validateSymbol(this).__description__ + ')'; }),
  90. valueOf: d(function () { return validateSymbol(this); })
  91. });
  92. defineProperty(SymbolPolyfill.prototype, SymbolPolyfill.toPrimitive, d('', function () {
  93. var symbol = validateSymbol(this);
  94. if (typeof symbol === 'symbol') return symbol;
  95. return symbol.toString();
  96. }));
  97. defineProperty(SymbolPolyfill.prototype, SymbolPolyfill.toStringTag, d('c', 'Symbol'));
  98. // Proper implementaton of toPrimitive and toStringTag for returned symbol instances
  99. defineProperty(HiddenSymbol.prototype, SymbolPolyfill.toStringTag,
  100. d('c', SymbolPolyfill.prototype[SymbolPolyfill.toStringTag]));
  101. // Note: It's important to define `toPrimitive` as last one, as some implementations
  102. // implement `toPrimitive` natively without implementing `toStringTag` (or other specified symbols)
  103. // And that may invoke error in definition flow:
  104. // See: https://github.com/medikoo/es6-symbol/issues/13#issuecomment-164146149
  105. defineProperty(HiddenSymbol.prototype, SymbolPolyfill.toPrimitive,
  106. d('c', SymbolPolyfill.prototype[SymbolPolyfill.toPrimitive]));