object.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. describe('Object', function () {
  2. var ifShimIt = (typeof process !== 'undefined' && process.env.NO_ES6_SHIM) ? it.skip : it;
  3. ifShimIt('is on the exported object', function () {
  4. var exported = require('../');
  5. expect(exported.Object).to.equal(Object);
  6. });
  7. var functionsHaveNames = (function foo() {}).name === 'foo';
  8. var ifFunctionsHaveNamesIt = functionsHaveNames ? it : xit;
  9. var ifExtensionsPreventable = Object.preventExtensions ? it : xit;
  10. var hasSymbols = typeof Symbol === 'function' && typeof Symbol() === 'symbol';
  11. var ifSymbolsIt = hasSymbols ? it : xit;
  12. var ifBrowserIt = typeof window === 'object' && typeof document === 'object' ? it : xit;
  13. var ifObjectGetPrototypeOfIt = typeof Object.getPrototypeOf === 'function' ? it : xit;
  14. if (Object.getOwnPropertyNames) {
  15. describe('.getOwnPropertyNames()', function () {
  16. it('throws on null or undefined', function () {
  17. expect(function () { Object.getOwnPropertyNames(); }).to['throw'](TypeError);
  18. expect(function () { Object.getOwnPropertyNames(undefined); }).to['throw'](TypeError);
  19. expect(function () { Object.getOwnPropertyNames(null); }).to['throw'](TypeError);
  20. });
  21. it('works on primitives', function () {
  22. [true, false, NaN, 42, /a/g, 'foo'].forEach(function (item) {
  23. expect(Object.getOwnPropertyNames(item)).to.eql(Object.getOwnPropertyNames(Object(item)));
  24. });
  25. });
  26. ifBrowserIt('does not break when an iframe is added', function () {
  27. var div = document.createElement('div');
  28. div.innerHTML = '<iframe src="http://xkcd.com"></iframe>';
  29. document.body.appendChild(div);
  30. setTimeout(function () {
  31. document.body.removeChild(div);
  32. }, 0);
  33. expect(Array.isArray(Object.getOwnPropertyNames(window))).to.eql(true);
  34. });
  35. });
  36. }
  37. if (Object.getOwnPropertyDescriptor) {
  38. describe('.getOwnPropertyDescriptor()', function () {
  39. it('throws on null or undefined', function () {
  40. expect(function () { Object.getOwnPropertyDescriptor(); }).to['throw'](TypeError);
  41. expect(function () { Object.getOwnPropertyDescriptor(undefined); }).to['throw'](TypeError);
  42. expect(function () { Object.getOwnPropertyDescriptor(null); }).to['throw'](TypeError);
  43. });
  44. it('works on primitives', function () {
  45. [true, false, NaN, 42, /a/g, 'foo'].forEach(function (item) {
  46. expect(Object.getOwnPropertyDescriptor(item, 'foo')).to.eql(Object.getOwnPropertyDescriptor(Object(item), 'foo'));
  47. });
  48. });
  49. });
  50. }
  51. if (Object.seal) {
  52. describe('.seal()', function () {
  53. it('works on primitives', function () {
  54. [null, undefined, true, false, NaN, 42, 'foo'].forEach(function (item) {
  55. expect(Object.seal(item)).to.eql(item);
  56. });
  57. });
  58. });
  59. }
  60. if (Object.isSealed) {
  61. describe('.isSealed()', function () {
  62. it('works on primitives', function () {
  63. [null, undefined, true, false, NaN, 42, 'foo'].forEach(function (item) {
  64. expect(Object.isSealed(item)).to.equal(true);
  65. });
  66. });
  67. });
  68. }
  69. if (Object.freeze) {
  70. describe('.freeze()', function () {
  71. it('works on primitives', function () {
  72. [null, undefined, true, false, NaN, 42, 'foo'].forEach(function (item) {
  73. expect(Object.freeze(item)).to.eql(item);
  74. });
  75. });
  76. });
  77. }
  78. if (Object.isFrozen) {
  79. describe('.isFrozen()', function () {
  80. it('works on primitives', function () {
  81. [null, undefined, true, false, NaN, 42, 'foo'].forEach(function (item) {
  82. expect(Object.isFrozen(item)).to.equal(true);
  83. });
  84. });
  85. });
  86. }
  87. if (Object.preventExtensions) {
  88. describe('.preventExtensions()', function () {
  89. it('works on primitives', function () {
  90. [null, undefined, true, false, NaN, 42, 'foo'].forEach(function (item) {
  91. expect(Object.preventExtensions(item)).to.eql(item);
  92. });
  93. });
  94. });
  95. }
  96. if (Object.isExtensible) {
  97. describe('.isExtensible()', function () {
  98. it('works on primitives', function () {
  99. [null, undefined, true, false, NaN, 42, 'foo'].forEach(function (item) {
  100. expect(Object.isExtensible(item)).to.equal(false);
  101. });
  102. });
  103. });
  104. }
  105. describe('.keys()', function () {
  106. it('works on strings', function () {
  107. expect(Object.keys('foo')).to.eql(['0', '1', '2']);
  108. });
  109. it('throws on null or undefined', function () {
  110. expect(function () { Object.keys(); }).to['throw'](TypeError);
  111. expect(function () { Object.keys(undefined); }).to['throw'](TypeError);
  112. expect(function () { Object.keys(null); }).to['throw'](TypeError);
  113. });
  114. it('works on other primitives', function () {
  115. [true, false, NaN, 42, /a/g].forEach(function (item) {
  116. expect(Object.keys(item)).to.eql([]);
  117. });
  118. });
  119. });
  120. describe('.is()', function () {
  121. if (!Object.prototype.hasOwnProperty.call(Object, 'is')) {
  122. return it('exists', function () {
  123. expect(Object).to.have.property('is');
  124. });
  125. }
  126. ifFunctionsHaveNamesIt('has the right name', function () {
  127. expect(Object.is).to.have.property('name', 'is');
  128. });
  129. it('should have the right arity', function () {
  130. expect(Object.is).to.have.property('length', 2);
  131. });
  132. it('should compare regular objects correctly', function () {
  133. [null, undefined, [0], 5, 'str', { a: null }].map(function (item) {
  134. return Object.is(item, item);
  135. }).forEach(function (result) {
  136. expect(result).to.equal(true);
  137. });
  138. });
  139. it('should compare 0 and -0 correctly', function () {
  140. expect(Object.is(0, -0)).to.equal(false);
  141. });
  142. it('should compare NaNs correctly', function () {
  143. expect(Object.is(NaN, NaN)).to.equal(true);
  144. });
  145. });
  146. describe('.assign()', function () {
  147. if (!Object.prototype.hasOwnProperty.call(Object, 'assign')) {
  148. return it('exists', function () {
  149. expect(Object).to.have.property('assign');
  150. });
  151. }
  152. it('has the correct length', function () {
  153. expect(Object.assign.length).to.eql(2);
  154. });
  155. it('returns the modified target object', function () {
  156. var target = {};
  157. var returned = Object.assign(target, { a: 1 });
  158. expect(returned).to.equal(target);
  159. });
  160. it('should merge two objects', function () {
  161. var target = { a: 1 };
  162. var returned = Object.assign(target, { b: 2 });
  163. expect(returned).to.eql({ a: 1, b: 2 });
  164. });
  165. it('should merge three objects', function () {
  166. var target = { a: 1 };
  167. var source1 = { b: 2 };
  168. var source2 = { c: 3 };
  169. var returned = Object.assign(target, source1, source2);
  170. expect(returned).to.eql({ a: 1, b: 2, c: 3 });
  171. });
  172. it('only iterates over own keys', function () {
  173. var Foo = function () {};
  174. Foo.prototype.bar = true;
  175. var foo = new Foo();
  176. foo.baz = true;
  177. var target = { a: 1 };
  178. var returned = Object.assign(target, foo);
  179. expect(returned).to.equal(target);
  180. expect(target).to.eql({ baz: true, a: 1 });
  181. });
  182. it('throws when target is null or undefined', function () {
  183. expect(function () { Object.assign(null); }).to['throw'](TypeError);
  184. expect(function () { Object.assign(undefined); }).to['throw'](TypeError);
  185. });
  186. it('coerces lone target to an object', function () {
  187. var result = {
  188. bool: Object.assign(true),
  189. number: Object.assign(1),
  190. string: Object.assign('1')
  191. };
  192. expect(typeof result.bool).to.equal('object');
  193. expect(Boolean.prototype.valueOf.call(result.bool)).to.equal(true);
  194. expect(typeof result.number).to.equal('object');
  195. expect(Number.prototype.valueOf.call(result.number)).to.equal(1);
  196. expect(typeof result.string).to.equal('object');
  197. expect(String.prototype.valueOf.call(result.string)).to.equal('1');
  198. });
  199. it('coerces target to an object, assigns from sources', function () {
  200. var sourceA = { a: 1 };
  201. var sourceB = { b: 1 };
  202. var result = {
  203. bool: Object.assign(true, sourceA, sourceB),
  204. number: Object.assign(1, sourceA, sourceB),
  205. string: Object.assign('1', sourceA, sourceB)
  206. };
  207. expect(typeof result.bool).to.equal('object');
  208. expect(Boolean.prototype.valueOf.call(result.bool)).to.equal(true);
  209. expect(result.bool).to.eql({ a: 1, b: 1 });
  210. expect(typeof result.number).to.equal('object');
  211. expect(Number.prototype.valueOf.call(result.number)).to.equal(1);
  212. expect(typeof result.string).to.equal('object');
  213. expect(String.prototype.valueOf.call(result.string)).to.equal('1');
  214. expect(result.string).to.eql({ 0: '1', a: 1, b: 1 });
  215. });
  216. it('ignores non-object sources', function () {
  217. expect(Object.assign({ a: 1 }, null, { b: 2 })).to.eql({ a: 1, b: 2 });
  218. expect(Object.assign({ a: 1 }, undefined, { b: 2 })).to.eql({ a: 1, b: 2 });
  219. expect(Object.assign({ a: 1 }, { b: 2 }, null)).to.eql({ a: 1, b: 2 });
  220. });
  221. ifExtensionsPreventable('does not have pending exceptions', function () {
  222. 'use strict';
  223. // Firefox 37 still has "pending exception" logic in its Object.assign implementation,
  224. // which is 72% slower than our shim, and Firefox 40's native implementation.
  225. var thrower = { 1: 2, 2: 3, 3: 4 };
  226. Object.defineProperty(thrower, 2, {
  227. get: function () { return 3; },
  228. set: function (v) { throw new RangeError('IE 9 does not throw on preventExtensions: ' + v); }
  229. });
  230. Object.preventExtensions(thrower);
  231. expect(thrower).to.have.property(2, 3);
  232. var error;
  233. try {
  234. Object.assign(thrower, 'wxyz');
  235. } catch (e) {
  236. error = e;
  237. }
  238. expect(thrower).not.to.have.property(0);
  239. if (thrower[1] === 'x') {
  240. // IE 9 doesn't throw in strict mode with preventExtensions
  241. expect(error).to.be.an.instanceOf(RangeError);
  242. } else {
  243. expect(error).to.be.an.instanceOf(TypeError);
  244. expect(thrower).to.have.property(1, 2);
  245. }
  246. expect(thrower).to.have.property(2, 3);
  247. expect(thrower).to.have.property(3, 4);
  248. });
  249. ifSymbolsIt('includes enumerable symbols, after keys', function () {
  250. /* eslint max-statements-per-line: 1 */
  251. var visited = [];
  252. var obj = {};
  253. Object.defineProperty(obj, 'a', { get: function () { visited.push('a'); return 42; }, enumerable: true });
  254. var symbol = Symbol('enumerable');
  255. Object.defineProperty(obj, symbol, {
  256. get: function () { visited.push(symbol); return Infinity; },
  257. enumerable: true
  258. });
  259. var nonEnumSymbol = Symbol('non-enumerable');
  260. Object.defineProperty(obj, nonEnumSymbol, {
  261. get: function () { visited.push(nonEnumSymbol); return -Infinity; },
  262. enumerable: false
  263. });
  264. var target = Object.assign({}, obj);
  265. expect(visited).to.eql(['a', symbol]);
  266. expect(target.a).to.equal(42);
  267. expect(target[symbol]).to.equal(Infinity);
  268. expect(target[nonEnumSymbol]).not.to.equal(-Infinity);
  269. });
  270. });
  271. describe('Object.setPrototypeOf()', function () {
  272. if (!Object.setPrototypeOf) {
  273. return; // IE < 11
  274. }
  275. describe('argument checking', function () {
  276. it('should throw TypeError if first arg is not object', function () {
  277. var nonObjects = [null, undefined, true, false, 1, 3, 'foo'];
  278. nonObjects.forEach(function (value) {
  279. expect(function () { Object.setPrototypeOf(value); }).to['throw'](TypeError);
  280. });
  281. });
  282. it('should throw TypeError if second arg is not object or null', function () {
  283. expect(function () { Object.setPrototypeOf({}, null); }).not.to['throw'](TypeError);
  284. var invalidPrototypes = [true, false, 1, 3, 'foo'];
  285. invalidPrototypes.forEach(function (proto) {
  286. expect(function () { Object.setPrototypeOf({}, proto); }).to['throw'](TypeError);
  287. });
  288. });
  289. });
  290. describe('set prototype', function () {
  291. it('should work', function () {
  292. var Foo = function () {};
  293. var Bar = {};
  294. var foo = new Foo();
  295. expect(Object.getPrototypeOf(foo)).to.equal(Foo.prototype);
  296. var fooBar = Object.setPrototypeOf(foo, Bar);
  297. expect(fooBar).to.equal(foo);
  298. expect(Object.getPrototypeOf(foo)).to.equal(Bar);
  299. });
  300. it('should be able to set to null', function () {
  301. var Foo = function () {};
  302. var foo = new Foo();
  303. var fooNull = Object.setPrototypeOf(foo, null);
  304. expect(fooNull).to.equal(foo);
  305. expect(Object.getPrototypeOf(foo)).to.equal(null);
  306. });
  307. });
  308. });
  309. describe('.getPrototypeOf()', function () {
  310. ifObjectGetPrototypeOfIt('does not throw for a primitive', function () {
  311. expect(Object.getPrototypeOf(3)).to.equal(Number.prototype);
  312. });
  313. });
  314. });