index.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. /* !
  2. * type-detect
  3. * Copyright(c) 2013 jake luer <jake@alogicalparadox.com>
  4. * MIT Licensed
  5. */
  6. const promiseExists = typeof Promise === 'function';
  7. /* eslint-disable no-undef */
  8. const globalObject = typeof self === 'object' ? self : global; // eslint-disable-line id-blacklist
  9. const symbolExists = typeof Symbol !== 'undefined';
  10. const mapExists = typeof Map !== 'undefined';
  11. const setExists = typeof Set !== 'undefined';
  12. const weakMapExists = typeof WeakMap !== 'undefined';
  13. const weakSetExists = typeof WeakSet !== 'undefined';
  14. const dataViewExists = typeof DataView !== 'undefined';
  15. const symbolIteratorExists = symbolExists && typeof Symbol.iterator !== 'undefined';
  16. const symbolToStringTagExists = symbolExists && typeof Symbol.toStringTag !== 'undefined';
  17. const setEntriesExists = setExists && typeof Set.prototype.entries === 'function';
  18. const mapEntriesExists = mapExists && typeof Map.prototype.entries === 'function';
  19. const setIteratorPrototype = setEntriesExists && Object.getPrototypeOf(new Set().entries());
  20. const mapIteratorPrototype = mapEntriesExists && Object.getPrototypeOf(new Map().entries());
  21. const arrayIteratorExists = symbolIteratorExists && typeof Array.prototype[Symbol.iterator] === 'function';
  22. const arrayIteratorPrototype = arrayIteratorExists && Object.getPrototypeOf([][Symbol.iterator]());
  23. const stringIteratorExists = symbolIteratorExists && typeof String.prototype[Symbol.iterator] === 'function';
  24. const stringIteratorPrototype = stringIteratorExists && Object.getPrototypeOf(''[Symbol.iterator]());
  25. const toStringLeftSliceLength = 8;
  26. const toStringRightSliceLength = -1;
  27. /**
  28. * ### typeOf (obj)
  29. *
  30. * Uses `Object.prototype.toString` to determine the type of an object,
  31. * normalising behaviour across engine versions & well optimised.
  32. *
  33. * @param {Mixed} object
  34. * @return {String} object type
  35. * @api public
  36. */
  37. export default function typeDetect(obj) {
  38. /* ! Speed optimisation
  39. * Pre:
  40. * string literal x 3,039,035 ops/sec ±1.62% (78 runs sampled)
  41. * boolean literal x 1,424,138 ops/sec ±4.54% (75 runs sampled)
  42. * number literal x 1,653,153 ops/sec ±1.91% (82 runs sampled)
  43. * undefined x 9,978,660 ops/sec ±1.92% (75 runs sampled)
  44. * function x 2,556,769 ops/sec ±1.73% (77 runs sampled)
  45. * Post:
  46. * string literal x 38,564,796 ops/sec ±1.15% (79 runs sampled)
  47. * boolean literal x 31,148,940 ops/sec ±1.10% (79 runs sampled)
  48. * number literal x 32,679,330 ops/sec ±1.90% (78 runs sampled)
  49. * undefined x 32,363,368 ops/sec ±1.07% (82 runs sampled)
  50. * function x 31,296,870 ops/sec ±0.96% (83 runs sampled)
  51. */
  52. const typeofObj = typeof obj;
  53. if (typeofObj !== 'object') {
  54. return typeofObj;
  55. }
  56. /* ! Speed optimisation
  57. * Pre:
  58. * null x 28,645,765 ops/sec ±1.17% (82 runs sampled)
  59. * Post:
  60. * null x 36,428,962 ops/sec ±1.37% (84 runs sampled)
  61. */
  62. if (obj === null) {
  63. return 'null';
  64. }
  65. /* ! Spec Conformance
  66. * Test: `Object.prototype.toString.call(window)``
  67. * - Node === "[object global]"
  68. * - Chrome === "[object global]"
  69. * - Firefox === "[object Window]"
  70. * - PhantomJS === "[object Window]"
  71. * - Safari === "[object Window]"
  72. * - IE 11 === "[object Window]"
  73. * - IE Edge === "[object Window]"
  74. * Test: `Object.prototype.toString.call(this)``
  75. * - Chrome Worker === "[object global]"
  76. * - Firefox Worker === "[object DedicatedWorkerGlobalScope]"
  77. * - Safari Worker === "[object DedicatedWorkerGlobalScope]"
  78. * - IE 11 Worker === "[object WorkerGlobalScope]"
  79. * - IE Edge Worker === "[object WorkerGlobalScope]"
  80. */
  81. if (obj === globalObject) {
  82. return 'global';
  83. }
  84. /* ! Speed optimisation
  85. * Pre:
  86. * array literal x 2,888,352 ops/sec ±0.67% (82 runs sampled)
  87. * Post:
  88. * array literal x 22,479,650 ops/sec ±0.96% (81 runs sampled)
  89. */
  90. if (
  91. Array.isArray(obj) &&
  92. (symbolToStringTagExists === false || !(Symbol.toStringTag in obj))
  93. ) {
  94. return 'Array';
  95. }
  96. // Not caching existence of `window` and related properties due to potential
  97. // for `window` to be unset before tests in quasi-browser environments.
  98. if (typeof window === 'object' && window !== null) {
  99. /* ! Spec Conformance
  100. * (https://html.spec.whatwg.org/multipage/browsers.html#location)
  101. * WhatWG HTML$7.7.3 - The `Location` interface
  102. * Test: `Object.prototype.toString.call(window.location)``
  103. * - IE <=11 === "[object Object]"
  104. * - IE Edge <=13 === "[object Object]"
  105. */
  106. if (typeof window.location === 'object' && obj === window.location) {
  107. return 'Location';
  108. }
  109. /* ! Spec Conformance
  110. * (https://html.spec.whatwg.org/#document)
  111. * WhatWG HTML$3.1.1 - The `Document` object
  112. * Note: Most browsers currently adher to the W3C DOM Level 2 spec
  113. * (https://www.w3.org/TR/DOM-Level-2-HTML/html.html#ID-26809268)
  114. * which suggests that browsers should use HTMLTableCellElement for
  115. * both TD and TH elements. WhatWG separates these.
  116. * WhatWG HTML states:
  117. * > For historical reasons, Window objects must also have a
  118. * > writable, configurable, non-enumerable property named
  119. * > HTMLDocument whose value is the Document interface object.
  120. * Test: `Object.prototype.toString.call(document)``
  121. * - Chrome === "[object HTMLDocument]"
  122. * - Firefox === "[object HTMLDocument]"
  123. * - Safari === "[object HTMLDocument]"
  124. * - IE <=10 === "[object Document]"
  125. * - IE 11 === "[object HTMLDocument]"
  126. * - IE Edge <=13 === "[object HTMLDocument]"
  127. */
  128. if (typeof window.document === 'object' && obj === window.document) {
  129. return 'Document';
  130. }
  131. if (typeof window.navigator === 'object') {
  132. /* ! Spec Conformance
  133. * (https://html.spec.whatwg.org/multipage/webappapis.html#mimetypearray)
  134. * WhatWG HTML$8.6.1.5 - Plugins - Interface MimeTypeArray
  135. * Test: `Object.prototype.toString.call(navigator.mimeTypes)``
  136. * - IE <=10 === "[object MSMimeTypesCollection]"
  137. */
  138. if (typeof window.navigator.mimeTypes === 'object' &&
  139. obj === window.navigator.mimeTypes) {
  140. return 'MimeTypeArray';
  141. }
  142. /* ! Spec Conformance
  143. * (https://html.spec.whatwg.org/multipage/webappapis.html#pluginarray)
  144. * WhatWG HTML$8.6.1.5 - Plugins - Interface PluginArray
  145. * Test: `Object.prototype.toString.call(navigator.plugins)``
  146. * - IE <=10 === "[object MSPluginsCollection]"
  147. */
  148. if (typeof window.navigator.plugins === 'object' &&
  149. obj === window.navigator.plugins) {
  150. return 'PluginArray';
  151. }
  152. }
  153. if ((typeof window.HTMLElement === 'function' ||
  154. typeof window.HTMLElement === 'object') &&
  155. obj instanceof window.HTMLElement) {
  156. /* ! Spec Conformance
  157. * (https://html.spec.whatwg.org/multipage/webappapis.html#pluginarray)
  158. * WhatWG HTML$4.4.4 - The `blockquote` element - Interface `HTMLQuoteElement`
  159. * Test: `Object.prototype.toString.call(document.createElement('blockquote'))``
  160. * - IE <=10 === "[object HTMLBlockElement]"
  161. */
  162. if (obj.tagName === 'BLOCKQUOTE') {
  163. return 'HTMLQuoteElement';
  164. }
  165. /* ! Spec Conformance
  166. * (https://html.spec.whatwg.org/#htmltabledatacellelement)
  167. * WhatWG HTML$4.9.9 - The `td` element - Interface `HTMLTableDataCellElement`
  168. * Note: Most browsers currently adher to the W3C DOM Level 2 spec
  169. * (https://www.w3.org/TR/DOM-Level-2-HTML/html.html#ID-82915075)
  170. * which suggests that browsers should use HTMLTableCellElement for
  171. * both TD and TH elements. WhatWG separates these.
  172. * Test: Object.prototype.toString.call(document.createElement('td'))
  173. * - Chrome === "[object HTMLTableCellElement]"
  174. * - Firefox === "[object HTMLTableCellElement]"
  175. * - Safari === "[object HTMLTableCellElement]"
  176. */
  177. if (obj.tagName === 'TD') {
  178. return 'HTMLTableDataCellElement';
  179. }
  180. /* ! Spec Conformance
  181. * (https://html.spec.whatwg.org/#htmltableheadercellelement)
  182. * WhatWG HTML$4.9.9 - The `td` element - Interface `HTMLTableHeaderCellElement`
  183. * Note: Most browsers currently adher to the W3C DOM Level 2 spec
  184. * (https://www.w3.org/TR/DOM-Level-2-HTML/html.html#ID-82915075)
  185. * which suggests that browsers should use HTMLTableCellElement for
  186. * both TD and TH elements. WhatWG separates these.
  187. * Test: Object.prototype.toString.call(document.createElement('th'))
  188. * - Chrome === "[object HTMLTableCellElement]"
  189. * - Firefox === "[object HTMLTableCellElement]"
  190. * - Safari === "[object HTMLTableCellElement]"
  191. */
  192. if (obj.tagName === 'TH') {
  193. return 'HTMLTableHeaderCellElement';
  194. }
  195. }
  196. }
  197. /* ! Speed optimisation
  198. * Pre:
  199. * Float64Array x 625,644 ops/sec ±1.58% (80 runs sampled)
  200. * Float32Array x 1,279,852 ops/sec ±2.91% (77 runs sampled)
  201. * Uint32Array x 1,178,185 ops/sec ±1.95% (83 runs sampled)
  202. * Uint16Array x 1,008,380 ops/sec ±2.25% (80 runs sampled)
  203. * Uint8Array x 1,128,040 ops/sec ±2.11% (81 runs sampled)
  204. * Int32Array x 1,170,119 ops/sec ±2.88% (80 runs sampled)
  205. * Int16Array x 1,176,348 ops/sec ±5.79% (86 runs sampled)
  206. * Int8Array x 1,058,707 ops/sec ±4.94% (77 runs sampled)
  207. * Uint8ClampedArray x 1,110,633 ops/sec ±4.20% (80 runs sampled)
  208. * Post:
  209. * Float64Array x 7,105,671 ops/sec ±13.47% (64 runs sampled)
  210. * Float32Array x 5,887,912 ops/sec ±1.46% (82 runs sampled)
  211. * Uint32Array x 6,491,661 ops/sec ±1.76% (79 runs sampled)
  212. * Uint16Array x 6,559,795 ops/sec ±1.67% (82 runs sampled)
  213. * Uint8Array x 6,463,966 ops/sec ±1.43% (85 runs sampled)
  214. * Int32Array x 5,641,841 ops/sec ±3.49% (81 runs sampled)
  215. * Int16Array x 6,583,511 ops/sec ±1.98% (80 runs sampled)
  216. * Int8Array x 6,606,078 ops/sec ±1.74% (81 runs sampled)
  217. * Uint8ClampedArray x 6,602,224 ops/sec ±1.77% (83 runs sampled)
  218. */
  219. const stringTag = (symbolToStringTagExists && obj[Symbol.toStringTag]);
  220. if (typeof stringTag === 'string') {
  221. return stringTag;
  222. }
  223. const objPrototype = Object.getPrototypeOf(obj);
  224. /* ! Speed optimisation
  225. * Pre:
  226. * regex literal x 1,772,385 ops/sec ±1.85% (77 runs sampled)
  227. * regex constructor x 2,143,634 ops/sec ±2.46% (78 runs sampled)
  228. * Post:
  229. * regex literal x 3,928,009 ops/sec ±0.65% (78 runs sampled)
  230. * regex constructor x 3,931,108 ops/sec ±0.58% (84 runs sampled)
  231. */
  232. if (objPrototype === RegExp.prototype) {
  233. return 'RegExp';
  234. }
  235. /* ! Speed optimisation
  236. * Pre:
  237. * date x 2,130,074 ops/sec ±4.42% (68 runs sampled)
  238. * Post:
  239. * date x 3,953,779 ops/sec ±1.35% (77 runs sampled)
  240. */
  241. if (objPrototype === Date.prototype) {
  242. return 'Date';
  243. }
  244. /* ! Spec Conformance
  245. * (http://www.ecma-international.org/ecma-262/6.0/index.html#sec-promise.prototype-@@tostringtag)
  246. * ES6$25.4.5.4 - Promise.prototype[@@toStringTag] should be "Promise":
  247. * Test: `Object.prototype.toString.call(Promise.resolve())``
  248. * - Chrome <=47 === "[object Object]"
  249. * - Edge <=20 === "[object Object]"
  250. * - Firefox 29-Latest === "[object Promise]"
  251. * - Safari 7.1-Latest === "[object Promise]"
  252. */
  253. if (promiseExists && objPrototype === Promise.prototype) {
  254. return 'Promise';
  255. }
  256. /* ! Speed optimisation
  257. * Pre:
  258. * set x 2,222,186 ops/sec ±1.31% (82 runs sampled)
  259. * Post:
  260. * set x 4,545,879 ops/sec ±1.13% (83 runs sampled)
  261. */
  262. if (setExists && objPrototype === Set.prototype) {
  263. return 'Set';
  264. }
  265. /* ! Speed optimisation
  266. * Pre:
  267. * map x 2,396,842 ops/sec ±1.59% (81 runs sampled)
  268. * Post:
  269. * map x 4,183,945 ops/sec ±6.59% (82 runs sampled)
  270. */
  271. if (mapExists && objPrototype === Map.prototype) {
  272. return 'Map';
  273. }
  274. /* ! Speed optimisation
  275. * Pre:
  276. * weakset x 1,323,220 ops/sec ±2.17% (76 runs sampled)
  277. * Post:
  278. * weakset x 4,237,510 ops/sec ±2.01% (77 runs sampled)
  279. */
  280. if (weakSetExists && objPrototype === WeakSet.prototype) {
  281. return 'WeakSet';
  282. }
  283. /* ! Speed optimisation
  284. * Pre:
  285. * weakmap x 1,500,260 ops/sec ±2.02% (78 runs sampled)
  286. * Post:
  287. * weakmap x 3,881,384 ops/sec ±1.45% (82 runs sampled)
  288. */
  289. if (weakMapExists && objPrototype === WeakMap.prototype) {
  290. return 'WeakMap';
  291. }
  292. /* ! Spec Conformance
  293. * (http://www.ecma-international.org/ecma-262/6.0/index.html#sec-dataview.prototype-@@tostringtag)
  294. * ES6$24.2.4.21 - DataView.prototype[@@toStringTag] should be "DataView":
  295. * Test: `Object.prototype.toString.call(new DataView(new ArrayBuffer(1)))``
  296. * - Edge <=13 === "[object Object]"
  297. */
  298. if (dataViewExists && objPrototype === DataView.prototype) {
  299. return 'DataView';
  300. }
  301. /* ! Spec Conformance
  302. * (http://www.ecma-international.org/ecma-262/6.0/index.html#sec-%mapiteratorprototype%-@@tostringtag)
  303. * ES6$23.1.5.2.2 - %MapIteratorPrototype%[@@toStringTag] should be "Map Iterator":
  304. * Test: `Object.prototype.toString.call(new Map().entries())``
  305. * - Edge <=13 === "[object Object]"
  306. */
  307. if (mapExists && objPrototype === mapIteratorPrototype) {
  308. return 'Map Iterator';
  309. }
  310. /* ! Spec Conformance
  311. * (http://www.ecma-international.org/ecma-262/6.0/index.html#sec-%setiteratorprototype%-@@tostringtag)
  312. * ES6$23.2.5.2.2 - %SetIteratorPrototype%[@@toStringTag] should be "Set Iterator":
  313. * Test: `Object.prototype.toString.call(new Set().entries())``
  314. * - Edge <=13 === "[object Object]"
  315. */
  316. if (setExists && objPrototype === setIteratorPrototype) {
  317. return 'Set Iterator';
  318. }
  319. /* ! Spec Conformance
  320. * (http://www.ecma-international.org/ecma-262/6.0/index.html#sec-%arrayiteratorprototype%-@@tostringtag)
  321. * ES6$22.1.5.2.2 - %ArrayIteratorPrototype%[@@toStringTag] should be "Array Iterator":
  322. * Test: `Object.prototype.toString.call([][Symbol.iterator]())``
  323. * - Edge <=13 === "[object Object]"
  324. */
  325. if (arrayIteratorExists && objPrototype === arrayIteratorPrototype) {
  326. return 'Array Iterator';
  327. }
  328. /* ! Spec Conformance
  329. * (http://www.ecma-international.org/ecma-262/6.0/index.html#sec-%stringiteratorprototype%-@@tostringtag)
  330. * ES6$21.1.5.2.2 - %StringIteratorPrototype%[@@toStringTag] should be "String Iterator":
  331. * Test: `Object.prototype.toString.call(''[Symbol.iterator]())``
  332. * - Edge <=13 === "[object Object]"
  333. */
  334. if (stringIteratorExists && objPrototype === stringIteratorPrototype) {
  335. return 'String Iterator';
  336. }
  337. /* ! Speed optimisation
  338. * Pre:
  339. * object from null x 2,424,320 ops/sec ±1.67% (76 runs sampled)
  340. * Post:
  341. * object from null x 5,838,000 ops/sec ±0.99% (84 runs sampled)
  342. */
  343. if (objPrototype === null) {
  344. return 'Object';
  345. }
  346. return Object
  347. .prototype
  348. .toString
  349. .call(obj)
  350. .slice(toStringLeftSliceLength, toStringRightSliceLength);
  351. }