s-object.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. /* global window */
  2. var has = Object.prototype.hasOwnProperty;
  3. var supportsDescriptors = Object.defineProperty && (function () {
  4. try {
  5. var obj = {};
  6. Object.defineProperty(obj, 'x', { enumerable: false, value: obj });
  7. // eslint-disable-next-line no-unreachable-loop
  8. for (var _ in obj) { return false; } // jscs:ignore disallowUnusedVariables
  9. return obj.x === obj;
  10. } catch (e) { /* this is ES3 */
  11. return false;
  12. }
  13. }());
  14. var ifSupportsDescriptorsIt = supportsDescriptors ? it : xit;
  15. var ifWindowIt = typeof window === 'undefined' ? xit : it;
  16. var extensionsPreventible = typeof Object.preventExtensions === 'function' && (function () {
  17. var obj = {};
  18. Object.preventExtensions(obj);
  19. obj.a = 3;
  20. return obj.a !== 3;
  21. }());
  22. var ifExtensionsPreventibleIt = extensionsPreventible ? it : xit;
  23. var canSeal = typeof Object.seal === 'function' && (function () {
  24. var obj = { a: 3 };
  25. Object.seal(obj);
  26. delete obj.a;
  27. return obj.a === 3;
  28. }());
  29. var ifCanSealIt = canSeal ? it : xit;
  30. var canFreeze = typeof Object.freeze === 'function' && (function () {
  31. var obj = {};
  32. Object.freeze(obj);
  33. obj.a = 3;
  34. return obj.a !== 3;
  35. }());
  36. var ifCanFreezeIt = canFreeze ? it : xit;
  37. describe('Object', function () {
  38. 'use strict';
  39. describe('.keys()', function () {
  40. var obj = {
  41. str: 'boz',
  42. obj: { },
  43. arr: [],
  44. bool: true,
  45. num: 42,
  46. 'null': null,
  47. undefined: undefined
  48. };
  49. var loopedValues = [];
  50. for (var key in obj) {
  51. loopedValues.push(key);
  52. }
  53. var keys = Object.keys(obj);
  54. it('should have correct length', function () {
  55. expect(keys.length).toBe(7);
  56. });
  57. describe('arguments objects', function () {
  58. it('works with an arguments object', function () {
  59. (function () {
  60. expect(arguments.length).toBe(3);
  61. expect(Object.keys(arguments).length).toBe(arguments.length);
  62. expect(Object.keys(arguments)).toEqual(['0', '1', '2']);
  63. }(1, 2, 3));
  64. });
  65. it('works with a legacy arguments object', function () {
  66. var FakeArguments = function (args) {
  67. args.forEach(function (arg, i) {
  68. this[i] = arg;
  69. }.bind(this));
  70. };
  71. FakeArguments.prototype.length = 3;
  72. FakeArguments.prototype.callee = function () {};
  73. var fakeOldArguments = new FakeArguments(['a', 'b', 'c']);
  74. expect(Object.keys(fakeOldArguments)).toEqual(['0', '1', '2']);
  75. });
  76. });
  77. it('should return an Array', function () {
  78. expect(Array.isArray(keys)).toBe(true);
  79. });
  80. it('should return names which are own properties', function () {
  81. keys.forEach(function (name) {
  82. expect(has.call(obj, name)).toBe(true);
  83. });
  84. });
  85. it('should return names which are enumerable', function () {
  86. keys.forEach(function (name) {
  87. expect(loopedValues.indexOf(name)).toNotBe(-1);
  88. });
  89. });
  90. // ES6 Object.keys does not require this throw
  91. xit('should throw error for non object', function () {
  92. var e = {};
  93. expect(function () {
  94. try {
  95. Object.keys(42);
  96. } catch (err) {
  97. throw e;
  98. }
  99. }).toThrow(e);
  100. });
  101. describe('enumerating over non-enumerable properties', function () {
  102. it('has no enumerable keys on a Function', function () {
  103. var Foo = function () {};
  104. expect(Object.keys(Foo.prototype)).toEqual([]);
  105. });
  106. it('has no enumerable keys on a boolean', function () {
  107. expect(Object.keys(Boolean.prototype)).toEqual([]);
  108. });
  109. it('has no enumerable keys on an object', function () {
  110. expect(Object.keys(Object.prototype)).toEqual([]);
  111. });
  112. });
  113. it('works with boxed primitives', function () {
  114. expect(Object.keys(Object('hello'))).toEqual(['0', '1', '2', '3', '4']);
  115. });
  116. it('works with boxed primitives with extra properties', function () {
  117. var x = Object('x');
  118. x.y = 1;
  119. var actual = Object.keys(x);
  120. var expected = ['0', 'y'];
  121. actual.sort();
  122. expected.sort();
  123. expect(actual).toEqual(expected);
  124. });
  125. ifWindowIt('can serialize all objects on the `window`', function () {
  126. var windowItemKeys, exception;
  127. var excludedKeys = ['window', 'console', 'parent', 'self', 'frame', 'frames', 'frameElement', 'external', 'height', 'width', 'top', 'localStorage', 'applicationCache'];
  128. if (supportsDescriptors) {
  129. Object.defineProperty(window, 'thrower', {
  130. configurable: true,
  131. get: function () { throw new RangeError('thrower!'); }
  132. });
  133. }
  134. for (var k in window) {
  135. exception = void 0;
  136. windowItemKeys = exception;
  137. if (excludedKeys.indexOf(k) === -1 && has.call(window, k) && window[k] !== null && typeof window[k] === 'object') {
  138. try {
  139. windowItemKeys = Object.keys(window[k]);
  140. } catch (e) {
  141. exception = e;
  142. }
  143. expect(Array.isArray(windowItemKeys)).toBe(true);
  144. expect(exception).toBeUndefined();
  145. }
  146. }
  147. if (supportsDescriptors) {
  148. delete window.thrower;
  149. }
  150. });
  151. });
  152. describe('.isExtensible()', function () {
  153. var obj = { };
  154. it('should return true if object is extensible', function () {
  155. expect(Object.isExtensible(obj)).toBe(true);
  156. });
  157. ifExtensionsPreventibleIt('should return false if object is not extensible', function () {
  158. expect(Object.isExtensible(Object.preventExtensions(obj))).toBe(false);
  159. });
  160. ifCanSealIt('should return false if object is sealed', function () {
  161. expect(Object.isExtensible(Object.seal(obj))).toBe(false);
  162. });
  163. ifCanFreezeIt('should return false if object is frozen', function () {
  164. expect(Object.isExtensible(Object.freeze(obj))).toBe(false);
  165. });
  166. it('should throw error for non object', function () {
  167. try {
  168. // note: in ES6, this is expected to return false.
  169. expect(Object.isExtensible(42)).toBe(false);
  170. } catch (err) {
  171. expect(err).toEqual(jasmine.any(TypeError));
  172. }
  173. });
  174. });
  175. describe('.defineProperty()', function () {
  176. var obj;
  177. beforeEach(function () {
  178. obj = {};
  179. Object.defineProperty(obj, 'name', {
  180. value: 'Testing',
  181. configurable: true,
  182. enumerable: true,
  183. writable: true
  184. });
  185. });
  186. it('should return the initial value', function () {
  187. expect(has.call(obj, 'name')).toBeTruthy();
  188. expect(obj.name).toBe('Testing');
  189. });
  190. it('should be setable', function () {
  191. obj.name = 'Other';
  192. expect(obj.name).toBe('Other');
  193. });
  194. it('should return the parent initial value', function () {
  195. var child = Object.create(obj, {});
  196. expect(child.name).toBe('Testing');
  197. expect(has.call(child, 'name')).toBeFalsy();
  198. });
  199. it('should not override the parent value', function () {
  200. var child = Object.create(obj, {});
  201. Object.defineProperty(child, 'name', { value: 'Other' });
  202. expect(obj.name).toBe('Testing');
  203. expect(child.name).toBe('Other');
  204. });
  205. it('should throw error for non object', function () {
  206. expect(function () {
  207. Object.defineProperty(42, 'name', {});
  208. }).toThrow();
  209. });
  210. it('should not throw error for empty descriptor', function () {
  211. expect(function () {
  212. Object.defineProperty({}, 'name', {});
  213. }).not.toThrow();
  214. });
  215. ifSupportsDescriptorsIt('allows setting a nonwritable prototype', function () {
  216. var F = function () {};
  217. expect(F.prototype).toEqual(Object.getOwnPropertyDescriptor(F, 'prototype').value);
  218. expect((new F()).toString).toEqual(Object.prototype.toString);
  219. F.prototype = Number.prototype;
  220. expect(F.prototype).toEqual(Object.getOwnPropertyDescriptor(F, 'prototype').value);
  221. expect((new F()).toString).toEqual(Number.prototype.toString);
  222. var toStringSentinel = {};
  223. var sentinel = { toString: toStringSentinel };
  224. Object.defineProperty(F, 'prototype', { value: sentinel, writable: false });
  225. expect(F.prototype).toEqual(Object.getOwnPropertyDescriptor(F, 'prototype').value);
  226. expect((new F()).toString).toEqual(toStringSentinel);
  227. });
  228. ifSupportsDescriptorsIt('properly makes a prototype non-writable', function () {
  229. var O = { prototype: 1 };
  230. expect(O.prototype).toEqual(Object.getOwnPropertyDescriptor(O, 'prototype').value);
  231. expect(O.prototype).toEqual(1);
  232. expect(function () {
  233. Object.defineProperty(O, 'prototype', { writable: false, configurable: true });
  234. }).not.toThrow();
  235. var sentinel = {};
  236. expect(function () {
  237. Object.defineProperty(O, 'prototype', { value: sentinel });
  238. }).not.toThrow();
  239. expect(O.prototype).toEqual(Object.getOwnPropertyDescriptor(O, 'prototype').value);
  240. expect(O.prototype).toEqual(sentinel);
  241. });
  242. });
  243. describe('.getOwnPropertyDescriptor()', function () {
  244. it('should return undefined because the object does not own the property', function () {
  245. var descr = Object.getOwnPropertyDescriptor({}, 'name');
  246. expect(descr).toBeUndefined();
  247. });
  248. it('should return a data descriptor', function () {
  249. var descr = Object.getOwnPropertyDescriptor({ name: 'Testing' }, 'name');
  250. var expected = {
  251. value: 'Testing',
  252. enumerable: true,
  253. writable: true,
  254. configurable: true
  255. };
  256. expect(descr).toEqual(expected);
  257. });
  258. it('should return undefined because the object does not own the property', function () {
  259. var descr = Object.getOwnPropertyDescriptor(Object.create({ name: 'Testing' }, {}), 'name');
  260. expect(descr).toBeUndefined();
  261. });
  262. it('should return a data descriptor', function () {
  263. var expected = {
  264. value: 'Testing',
  265. configurable: true,
  266. enumerable: true,
  267. writable: true
  268. };
  269. var obj = Object.create({}, { name: expected });
  270. var descr = Object.getOwnPropertyDescriptor(obj, 'name');
  271. expect(descr).toEqual(expected);
  272. });
  273. it('should throw error for non object', function () {
  274. try {
  275. // note: in ES6, we expect this to return undefined.
  276. expect(Object.getOwnPropertyDescriptor(42, 'name')).toBeUndefined();
  277. } catch (err) {
  278. expect(err).toEqual(jasmine.any(TypeError));
  279. }
  280. });
  281. });
  282. describe('.getPrototypeOf()', function () {
  283. it('should return the [[Prototype]] of an object', function () {
  284. var Foo = function () {};
  285. var proto = Object.getPrototypeOf(new Foo());
  286. expect(proto).toBe(Foo.prototype);
  287. });
  288. it('should shamone to the `Object.prototype` if `object.constructor` is not a function', function () {
  289. var Foo = function () {};
  290. Foo.prototype.constructor = 1;
  291. var proto = Object.getPrototypeOf(new Foo()),
  292. fnToString = Function.prototype.toString;
  293. if (fnToString.call(Object.getPrototypeOf).indexOf('[native code]') < 0) {
  294. expect(proto).toBe(Object.prototype);
  295. } else {
  296. expect(proto).toBe(Foo.prototype);
  297. }
  298. });
  299. it('should throw error for non object', function () {
  300. try {
  301. expect(Object.getPrototypeOf(1)).toBe(Number.prototype); // ES6 behavior
  302. } catch (err) {
  303. expect(err).toEqual(jasmine.any(TypeError));
  304. }
  305. });
  306. it('should return null on Object.create(null)', function () {
  307. var obj = Object.create(null);
  308. expect(Object.getPrototypeOf(obj) === null).toBe(true);
  309. });
  310. });
  311. describe('.defineProperties()', function () {
  312. it('should define the constructor property', function () {
  313. var target = {};
  314. var newProperties = { constructor: { value: 'new constructor' } };
  315. Object.defineProperties(target, newProperties);
  316. expect(target.constructor).toBe('new constructor');
  317. });
  318. });
  319. describe('.create()', function () {
  320. it('should create objects with no properties when called as `Object.create(null)`', function () {
  321. var obj = Object.create(null);
  322. expect('hasOwnProperty' in obj).toBe(false);
  323. expect('toString' in obj).toBe(false);
  324. expect('constructor' in obj).toBe(false);
  325. var protoIsEnumerable = false;
  326. for (var k in obj) {
  327. if (k === '__proto__') {
  328. protoIsEnumerable = true;
  329. }
  330. }
  331. expect(protoIsEnumerable).toBe(false);
  332. expect(obj instanceof Object).toBe(false);
  333. });
  334. });
  335. });