]> Lady’s Gitweb - Pisces/blob - object.js
Add methods for own entries and values to object.js
[Pisces] / object.js
1 // ♓🌟 Piscēs ∷ object.js
2 // ====================================================================
3 //
4 // Copyright © 2022–2023 Lady [@ Lady’s Computer].
5 //
6 // This Source Code Form is subject to the terms of the Mozilla Public
7 // License, v. 2.0. If a copy of the MPL was not distributed with this
8 // file, You can obtain one at <https://mozilla.org/MPL/2.0/>.
9
10 import {
11 IS_CONCAT_SPREADABLE,
12 isAccessorDescriptor,
13 isDataDescriptor,
14 SPECIES,
15 toFunctionName,
16 toLength,
17 type,
18 UNDEFINED,
19 } from "./value.js";
20
21 const createArray = Array;
22 const { isArray } = Array;
23 const object = Object;
24 const {
25 assign,
26 create,
27 defineProperty,
28 defineProperties,
29 entries,
30 freeze: objectFreeze,
31 fromEntries,
32 getOwnPropertyDescriptor: objectGetOwnPropertyDescriptor,
33 getOwnPropertyNames,
34 getOwnPropertySymbols: objectGetOwnPropertySymbols,
35 getPrototypeOf,
36 hasOwn,
37 isExtensible,
38 isFrozen,
39 isSealed,
40 keys,
41 preventExtensions: objectPreventExtensions,
42 seal: objectSeal,
43 setPrototypeOf,
44 values,
45 } = Object;
46 const {
47 apply: call,
48 deleteProperty,
49 defineProperty: reflectDefineProperty,
50 get,
51 getOwnPropertyDescriptor: reflectGetOwnPropertyDescriptor,
52 has,
53 ownKeys,
54 set,
55 setPrototypeOf: reflectSetPrototypeOf,
56 } = Reflect;
57
58 /**
59 * An object whose properties are lazy‐loaded from the methods on the
60 * own properties of the provided object.
61 *
62 * This is useful when you are looking to reference properties on
63 * objects which, due to module dependency graphs, cannot be guaranteed
64 * to have been initialized yet.
65 *
66 * The resulting properties will have the same attributes (regarding
67 * configurability, enumerability, and writability) as the
68 * corresponding properties on the methods object. If a property is
69 * marked as writable, the method will never be called if it is set
70 * before it is gotten. By necessity, the resulting properties are all
71 * configurable before they are accessed for the first time.
72 *
73 * Methods will be called with the resulting object as their this
74 * value.
75 *
76 * `LazyLoader` objects have the same prototype as the passed methods
77 * object.
78 */
79 export class LazyLoader extends null {
80 /**
81 * Constructs a new `LazyLoader` object.
82 *
83 * ☡ This function throws if the provided value is not an object.
84 */
85 constructor(loadMethods) {
86 if (type(loadMethods) !== "object") {
87 // The provided value is not an object; throw an error.
88 throw new TypeError(
89 `Piscēs: Cannot construct LazyLoader: Provided argument is not an object: ${loadMethods}.`,
90 );
91 } else {
92 // The provided value is an object; process it and build the
93 // result.
94 const result = create(getPrototypeOf(loadMethods));
95 const methodKeys = ownKeys(loadMethods);
96 for (let index = 0; index < methodKeys.length; ++index) {
97 // Iterate over the property keys of the provided object and
98 // define getters and setters appropriately on the result.
99 const methodKey = methodKeys[index];
100 const { configurable, enumerable, writable } =
101 getOwnPropertyDescriptor(loadMethods, methodKey);
102 defineProperty(
103 result,
104 methodKey,
105 assign(create(null), {
106 configurable: true,
107 enumerable,
108 get: defineProperty(
109 () => {
110 const value = call(loadMethods[methodKey], result, []);
111 defineProperty(
112 result,
113 methodKey,
114 assign(create(null), {
115 configurable,
116 enumerable,
117 value,
118 writable,
119 }),
120 );
121 return value;
122 },
123 "name",
124 assign(create(null), {
125 value: toFunctionName(methodKey, "get"),
126 }),
127 ),
128 set: writable
129 ? defineProperty(
130 ($) =>
131 defineProperty(
132 result,
133 methodKey,
134 assign(create(null), {
135 configurable,
136 enumerable,
137 value: $,
138 writable,
139 }),
140 ),
141 "name",
142 assign(create(null), {
143 value: toFunctionName(methodKey, "set"),
144 }),
145 )
146 : UNDEFINED,
147 }),
148 );
149 }
150 return result;
151 }
152 }
153 }
154
155 /**
156 * Defines an own enumerable data property on the provided object with
157 * the provided property key and value.
158 */
159 export const defineOwnDataProperty = (O, P, V) =>
160 defineProperty(
161 O,
162 P,
163 assign(create(null), {
164 configurable: true,
165 enumerable: true,
166 value: V,
167 writable: true,
168 }),
169 );
170
171 /**
172 * Defines an own nonenumerable data property on the provided object
173 * with the provided property key and value.
174 */
175 export const defineOwnNonenumerableDataProperty = (O, P, V) =>
176 defineProperty(
177 O,
178 P,
179 assign(create(null), {
180 configurable: true,
181 enumerable: false,
182 value: V,
183 writable: true,
184 }),
185 );
186
187 /**
188 * Defines an own property on the provided object on the provided
189 * property key using the provided property descriptor.
190 *
191 * ※ This is effectively an alias for `Object.defineProperty`.
192 */
193 export const defineOwnProperty = (O, P, Desc) =>
194 defineProperty(O, P, Desc);
195
196 /**
197 * Defines own properties on the provided object using the descriptors
198 * on the enumerable own properties of the provided additional objects.
199 *
200 * ※ This differs from `Object.defineProperties` in that it can take
201 * multiple source objects.
202 */
203 export const defineOwnProperties = (O, ...sources) => {
204 const { length } = sources;
205 for (let k = 0; k < length; ++k) {
206 defineProperties(O, sources[k]);
207 }
208 return O;
209 };
210
211 /**
212 * Removes the provided property key from the provided object and
213 * returns the object.
214 *
215 * ※ This function differs from `Reflect.deleteProperty` and the
216 * `delete` operator in that it throws if the deletion is
217 * unsuccessful.
218 *
219 * ☡ This function throws if the first argument is not an object.
220 */
221 export const deleteOwnProperty = (O, P) => {
222 if (type(O) !== "object") {
223 throw new TypeError(
224 `Piscēs: Tried to set property but provided value was not an object: ${V}`,
225 );
226 } else if (!deleteProperty(O, P)) {
227 throw new TypeError(
228 `Piscēs: Tried to delete property from object but [[Delete]] returned false: ${P}`,
229 );
230 } else {
231 return O;
232 }
233 };
234
235 /**
236 * Marks the provided object as non·extensible and marks all its
237 * properties as nonconfigurable and (if data properties) nonwritable,
238 * and returns the object.
239 *
240 * ※ This is effectively an alias for `Object.freeze`.
241 */
242 export const freeze = (O) => objectFreeze(O);
243
244 /**
245 * Returns a new frozen shallow copy of the enumerable own properties
246 * of the provided object, according to the following rules :—
247 *
248 * - For data properties, create a nonconfigurable, nonwritable
249 * property with the same value.
250 *
251 * - For accessor properties, create a nonconfigurable accessor
252 * property with the same getter *and* setter.
253 *
254 * The prototype for the resulting object will be taken from the
255 * `.prototype` property of the provided constructor, or the
256 * `.prototype` of the `.constructor` of the provided object if the
257 * provided constructor is undefined. If the used constructor has a
258 * nonnullish `.[Symbol.species]`, that will be used instead. If the
259 * used constructor or species is nullish or does not have a
260 * `.prototype` property, the prototype is set to null.
261 *
262 * ※ The prototype of the provided object itself is ignored.
263 */
264 export const frozenCopy = (O, constructor = O?.constructor) => {
265 if (O == null) {
266 // O is null or undefined.
267 throw new TypeError(
268 "Piscēs: Cannot copy properties of null or undefined.",
269 );
270 } else {
271 // O is not null or undefined.
272 //
273 // (If not provided, the constructor will be the value of getting
274 // the `.constructor` property of O.)
275 const species = constructor?.[SPECIES] ?? constructor;
276 const copy = create(
277 species == null || !("prototype" in species)
278 ? null
279 : species.prototype,
280 );
281 const keys = ownKeys(O);
282 for (let k = 0; k < keys.length; ++k) {
283 const P = keys[k];
284 const Desc = getOwnPropertyDescriptor(O, P);
285 if (Desc.enumerable) {
286 // P is an enumerable property.
287 defineProperty(
288 copy,
289 P,
290 assign(
291 create(null),
292 isAccessorDescriptor(Desc)
293 ? {
294 configurable: false,
295 enumerable: true,
296 get: Desc.get,
297 set: Desc.set,
298 }
299 : {
300 configurable: false,
301 enumerable: true,
302 value: Desc.value,
303 writable: false,
304 },
305 ),
306 );
307 } else {
308 // P is not an enumerable property.
309 /* do nothing */
310 }
311 }
312 return objectPreventExtensions(copy);
313 }
314 };
315
316 /**
317 * Returns the function on the provided value at the provided property
318 * key.
319 *
320 * ☡ This function throws if the provided property key does not have an
321 * associated value which is callable.
322 */
323 export const getMethod = (V, P) => {
324 const func = V[P];
325 if (func == null) {
326 return undefined;
327 } else if (typeof func !== "function") {
328 throw new TypeError(`Piscēs: Method not callable: ${P}`);
329 } else {
330 return func;
331 }
332 };
333
334 /**
335 * Returns the property descriptor record for the own property with the
336 * provided property key on the provided value, or null if none exists.
337 *
338 * ※ This is effectively an alias for
339 * `Object.getOwnPropertyDescriptor`, but the return value is a proxied
340 * object with null prototype.
341 */
342 export const getOwnPropertyDescriptor = (O, P) => {
343 const desc = objectGetOwnPropertyDescriptor(O, P);
344 return desc === UNDEFINED
345 ? UNDEFINED
346 : toPropertyDescriptorRecord(desc);
347 };
348
349 /**
350 * Returns the property descriptors for the own properties on the
351 * provided value.
352 *
353 * ※ This is effectively an alias for
354 * `Object.getOwnPropertyDescriptors`, but the values on the resulting
355 * object are proxied objects with null prototypes.
356 */
357 export const getOwnPropertyDescriptors = (O) => {
358 const obj = toObject(O);
359 const keys = ownKeys(obj);
360 const descriptors = {};
361 for (let k = 0; k < keys.length; ++k) {
362 // Iterate over the keys of the provided object and collect its
363 // descriptors.
364 const key = keys[k];
365 defineOwnDataProperty(
366 descriptors,
367 key,
368 getOwnPropertyDescriptor(O, key),
369 );
370 }
371 return descriptors;
372 };
373
374 /**
375 * Returns an array of own property entries on the provided value,
376 * using the provided receiver if given.
377 */
378 export const getOwnPropertyEntries = (O, Receiver = O) => {
379 const obj = toObject(O);
380 const keys = ownKeys(obj);
381 const target = Receiver === UNDEFINED ? obj : toObject(Receiver);
382 const result = createArray(keys.length);
383 for (let k = 0; k < keys.length; ++k) {
384 // Iterate over each key and add the corresponding entry to the
385 // result.
386 const key = keys[k];
387 defineOwnDataProperty(
388 result,
389 k,
390 [key, getOwnPropertyValue(obj, keys[k], target)],
391 );
392 }
393 return result;
394 };
395
396 /**
397 * Returns an array of own property keys on the provided value.
398 *
399 * ※ This is effectively an alias for `Reflect.ownKeys`, except that
400 * it does not require that the argument be an object.
401 */
402 export const getOwnPropertyKeys = (O) => ownKeys(toObject(O));
403
404 /**
405 * Returns an array of string‐valued own property keys on the
406 * provided value.
407 *
408 * ☡ This includes both enumerable and non·enumerable properties.
409 *
410 * ※ This is effectively an alias for `Object.getOwnPropertyNames`.
411 */
412 export const getOwnPropertyStrings = (O) => getOwnPropertyNames(O);
413
414 /**
415 * Returns an array of symbol‐valued own property keys on the
416 * provided value.
417 *
418 * ☡ This includes both enumerable and non·enumerable properties.
419 *
420 * ※ This is effectively an alias for
421 * `Object.getOwnPropertySymbols`.
422 */
423 export const getOwnPropertySymbols = (O) =>
424 objectGetOwnPropertySymbols(O);
425
426 /**
427 * Returns the value of the provided own property on the provided
428 * value using the provided receiver, or undefined if the provided
429 * property is not an own property on the provided value.
430 *
431 * ※ If the receiver is not provided, it defaults to the provided
432 * value.
433 */
434 export const getOwnPropertyValue = (O, P, Receiver = UNDEFINED) => {
435 const obj = toObject(O);
436 const desc = getOwnPropertyDescriptor(O, P);
437 if (desc === UNDEFINED) {
438 // The provided property is not an own property on the provided
439 // value; return undefined.
440 return UNDEFINED;
441 }
442 if (isDataDescriptor(desc)) {
443 // The provided property is a data property; return its value.
444 return desc.value;
445 } else {
446 // The provided property is an accessor property; get its value
447 // using the appropriate receiver.
448 const target = Receiver === UNDEFINED ? obj : toObject(Receiver);
449 return call(desc.get, target, []);
450 }
451 };
452
453 /**
454 * Returns an array of own property values on the provided value, using
455 * the provided receiver if given.
456 */
457 export const getOwnPropertyValues = (O, Receiver = O) => {
458 const obj = toObject(O);
459 const keys = ownKeys(obj);
460 const target = Receiver === UNDEFINED ? obj : toObject(Receiver);
461 const result = createArray(keys.length);
462 for (let k = 0; k < keys.length; ++k) {
463 // Iterate over each key and collect the values.
464 defineOwnDataProperty(
465 result,
466 k,
467 getOwnPropertyValue(obj, keys[k], target),
468 );
469 }
470 return result;
471 };
472
473 /**
474 * Returns the value of the provided property key on the provided
475 * value.
476 *
477 * ※ This is effectively an alias for `Reflect.get`, except that it
478 * does not require that the argument be an object.
479 */
480 export const getPropertyValue = (O, P, Receiver = O) =>
481 get(toObject(O), P, Receiver);
482
483 /**
484 * Returns the prototype of the provided value.
485 *
486 * ※ This is effectively an alias for `Object.getPrototypeOf`.
487 */
488 export const getPrototype = (O) => getPrototypeOf(O);
489
490 /**
491 * Returns whether the provided value has an own property with the
492 * provided property key.
493 *
494 * ※ This is effectively an alias for `Object.hasOwn`.
495 */
496 export const hasOwnProperty = (O, P) => hasOwn(O, P);
497
498 /**
499 * Returns whether the provided property key exists on the provided
500 * value.
501 *
502 * ※ This is effectively an alias for `Reflect.has`, except that it
503 * does not require that the argument be an object.
504 *
505 * ※ This includes properties present on the prototype chain.
506 */
507 export const hasProperty = (O, P) => has(toObject(O), P);
508
509 /** Returns whether the provided value is an arraylike object. */
510 export const isArraylikeObject = ($) => {
511 if (type($) !== "object") {
512 return false;
513 } else {
514 try {
515 lengthOfArraylike($); // throws if not arraylike
516 return true;
517 } catch {
518 return false;
519 }
520 }
521 };
522
523 /**
524 * Returns whether the provided value is spreadable during array
525 * concatenation.
526 *
527 * This is also used to determine which things should be treated as
528 * collections.
529 */
530 export const isConcatSpreadableObject = ($) => {
531 if (type($) !== "object") {
532 // The provided value is not an object.
533 return false;
534 } else {
535 // The provided value is an object.
536 const spreadable = $[IS_CONCAT_SPREADABLE];
537 return spreadable !== UNDEFINED ? !!spreadable : isArray($);
538 }
539 };
540
541 /**
542 * Returns whether the provided value is an extensible object.
543 *
544 * ※ This function returns false for nonobjects.
545 *
546 * ※ This is effectively an alias for `Object.isExtensible`.
547 */
548 export const isExtensibleObject = (O) => isExtensible(O);
549
550 export const {
551 /**
552 * Returns whether the provided value is a property descriptor record
553 * as created by `toPropertyDescriptor`.
554 *
555 * ※ This function is provided to enable inspection of whether an
556 * object uses the property descriptor record proxy implementation,
557 * not as a general test of whether an object satisfies the
558 * requirements for property descriptors. In most cases, a more
559 * specific test, like `isAccessorDescriptor`, `isDataDescriptor`, or
560 * `isGenericDescriptor`, is preferrable.
561 */
562 isPropertyDescriptorRecord,
563
564 /**
565 * Converts the provided value to a property descriptor record.
566 *
567 * ※ The prototype of a property descriptor record is always `null`.
568 *
569 * ※ Actually constructing a property descriptor object using this
570 * class is only necessary if you need strict guarantees about the
571 * types of its properties; the resulting object is proxied to ensure
572 * the types match what one would expect from composing
573 * FromPropertyDescriptor and ToPropertyDescriptor in the Ecmascript
574 * specification.
575 */
576 toPropertyDescriptorRecord,
577 } = (() => {
578 const proxyConstructor = Proxy;
579 const { add: weakSetAdd, has: weakSetHas } = WeakSet.prototype;
580 const propertyDescriptorRecords = new WeakSet();
581 const coercePropertyDescriptorValue = (P, V) => {
582 switch (P) {
583 case "configurable":
584 case "enumerable":
585 case "writable":
586 return !!V;
587 case "value":
588 return V;
589 case "get":
590 if (V !== undefined && typeof V !== "function") {
591 throw new TypeError(
592 "Piscēs: Getters must be callable.",
593 );
594 } else {
595 return V;
596 }
597 case "set":
598 if (V !== undefined && typeof V !== "function") {
599 throw new TypeError(
600 "Piscēs: Setters must be callable.",
601 );
602 } else {
603 return V;
604 }
605 default:
606 return V;
607 }
608 };
609 const propertyDescriptorProxyHandler = objectFreeze(
610 assign(
611 create(null),
612 {
613 defineProperty(O, P, Desc) {
614 if (
615 P === "configurable" || P === "enumerable" ||
616 P === "writable" || P === "value" ||
617 P === "get" || P === "set"
618 ) {
619 // P is a property descriptor attribute.
620 const desc = assign(objectCreate(null), Desc);
621 if ("get" in desc || "set" in desc) {
622 // Desc is an accessor property descriptor.
623 throw new TypeError(
624 "Piscēs: Property descriptor attributes must be data properties.",
625 );
626 } else if ("value" in desc || !(P in O)) {
627 // Desc has a value or P does not already exist on O.
628 desc.value = coercePropertyDescriptorValue(
629 P,
630 desc.value,
631 );
632 } else {
633 // Desc is not an accessor property descriptor and has no
634 // value, but an existing value is present on O.
635 /* do nothing */
636 }
637 const isAccessorDescriptor = "get" === P || "set" === P ||
638 "get" in O || "set" in O;
639 const isDataDescriptor = "value" === P ||
640 "writable" === P ||
641 "value" in O || "writable" in O;
642 if (isAccessorDescriptor && isDataDescriptor) {
643 // Both accessor and data attributes will be present on O
644 // after defining P.
645 throw new TypeError(
646 "Piscēs: Property descriptors cannot specify both accessor and data attributes.",
647 );
648 } else {
649 // P can be safely defined on O.
650 return reflectDefineProperty(O, P, desc);
651 }
652 } else {
653 // P is not a property descriptor attribute.
654 return reflectDefineProperty(O, P, Desc);
655 }
656 },
657 setPrototypeOf(O, V) {
658 if (V !== null) {
659 // V is not the property descriptor prototype.
660 return false;
661 } else {
662 // V is the property descriptor prototype.
663 return reflectSetPrototypeOf(O, V);
664 }
665 },
666 },
667 ),
668 );
669
670 return {
671 isPropertyDescriptorRecord: ($) =>
672 call(weakSetHas, propertyDescriptorRecords, [$]),
673 toPropertyDescriptorRecord: (Obj) => {
674 if (type(Obj) !== "object") {
675 // The provided value is not an object.
676 throw new TypeError(
677 `Piscēs: Cannot convert primitive to property descriptor: ${O}.`,
678 );
679 } else {
680 // The provided value is an object.
681 const desc = create(null);
682 if ("enumerable" in Obj) {
683 // An enumerable property is specified.
684 defineOwnDataProperty(desc, "enumerable", !!Obj.enumerable);
685 } else {
686 // An enumerable property is not specified.
687 /* do nothing */
688 }
689 if ("configurable" in Obj) {
690 // A configurable property is specified.
691 defineOwnDataProperty(
692 desc,
693 "configurable",
694 !!Obj.configurable,
695 );
696 } else {
697 // A configurable property is not specified.
698 /* do nothing */
699 }
700 if ("value" in Obj) {
701 // A value property is specified.
702 defineOwnDataProperty(desc, "value", Obj.value);
703 } else {
704 // A value property is not specified.
705 /* do nothing */
706 }
707 if ("writable" in Obj) {
708 // A writable property is specified.
709 defineOwnDataProperty(desc, "writable", !!Obj.writable);
710 } else {
711 // A writable property is not specified.
712 /* do nothing */
713 }
714 if ("get" in Obj) {
715 // A get property is specified.
716 const getter = Obj.get;
717 if (getter !== UNDEFINED && typeof getter !== "function") {
718 // The getter is not callable.
719 throw new TypeError("Piscēs: Getters must be callable.");
720 } else {
721 // The getter is callable.
722 defineOwnDataProperty(desc, "get", Obj.get);
723 }
724 } else {
725 // A get property is not specified.
726 /* do nothing */
727 }
728 if ("set" in Obj) {
729 // A set property is specified.
730 const setter = Obj.set;
731 if (setter !== UNDEFINED && typeof setter !== "function") {
732 // The setter is not callable.
733 throw new TypeError("Piscēs: Setters must be callable.");
734 } else {
735 // The setter is callable.
736 defineOwnDataProperty(desc, "set", Obj.set);
737 }
738 } else {
739 // A set property is not specified.
740 /* do nothing */
741 }
742 if (
743 ("get" in desc || "set" in desc) &&
744 ("value" in desc || "writable" in desc)
745 ) {
746 // Both accessor and data attributes have been defined.
747 throw new TypeError(
748 "Piscēs: Property descriptors cannot specify both accessor and data attributes.",
749 );
750 } else {
751 // The property descriptor is valid.
752 const record = new proxyConstructor(
753 desc,
754 propertyDescriptorProxyHandler,
755 );
756 call(weakSetAdd, propertyDescriptorRecords, [record]);
757 return record;
758 }
759 }
760 },
761 };
762 })();
763
764 /**
765 * Returns whether the provided value is an unfrozen object.
766 *
767 * ※ This function returns false for nonobjects.
768 *
769 * ※ This is effectively an alias for `!Object.isFrozen`.
770 */
771 export const isUnfrozenObject = (O) => !isFrozen(O);
772
773 /**
774 * Returns whether the provided value is an unsealed object.
775 *
776 * ※ This function returns false for nonobjects.
777 *
778 * ※ This is effectively an alias for `!Object.isSealed`.
779 */
780 export const isUnsealedObject = (O) => !isSealed(O);
781
782 /**
783 * Returns the length of the provided arraylike value.
784 *
785 * This can produce larger lengths than can actually be stored in
786 * arrays, because no such restrictions exist on arraylike methods.
787 *
788 * ☡ This function throws if the provided value is not arraylike.
789 */
790 export const lengthOfArraylike = ({ length }) => toLength(length);
791
792 /**
793 * Returns an array of key~value pairs for the enumerable,
794 * string‐valued property keys on the provided value.
795 *
796 * ※ This is effectively an alias for `Object.entries`.
797 */
798 export const namedEntries = (O) => entries(O);
799
800 /**
801 * Returns an array of the enumerable, string‐valued property keys on
802 * the provided value.
803 *
804 * ※ This is effectively an alias for `Object.keys`.
805 */
806 export const namedKeys = (O) => keys(O);
807
808 /**
809 * Returns an array of property values for the enumerable,
810 * string‐valued property keys on the provided value.
811 *
812 * ※ This is effectively an alias for `Object.values`.
813 */
814 export const namedValues = (O) => values(O);
815
816 /**
817 * Returns a new object with the provided prototype and property
818 * descriptors.
819 *
820 * ※ This is effectively an alias for `Object.create`.
821 */
822 export const objectCreate = (O, Properties) => create(O, Properties);
823
824 /**
825 * Returns a new object with property keys and values from the provided
826 * iterable value.
827 *
828 * ※ This is effectively an alias for `Object.fromEntries`.
829 */
830 export const objectFromEntries = (iterable) => fromEntries(iterable);
831
832 /**
833 * Marks the provided object as non·extensible, and returns the
834 * object.
835 *
836 * ※ This is effectively an alias for `Object.preventExtensions`.
837 */
838 export const preventExtensions = (O) => objectPreventExtensions(O);
839
840 /**
841 * Marks the provided object as non·extensible and marks all its
842 * properties as nonconfigurable, and returns the object.
843 *
844 * ※ This is effectively an alias for `Object.seal`.
845 */
846 export const seal = (O) => objectSeal(O);
847
848 /**
849 * Sets the provided property key to the provided value on the provided
850 * object and returns the object.
851 *
852 * ※ This function differs from `Reflect.set` in that it throws if the
853 * setting is unsuccessful.
854 *
855 * ☡ This function throws if the first argument is not an object.
856 */
857 export const setPropertyValue = (O, P, V, Receiver = O) => {
858 if (type(O) !== "object") {
859 throw new TypeError(
860 `Piscēs: Tried to set property but provided value was not an object: ${V}`,
861 );
862 } else if (!set(O, P, V, Receiver)) {
863 throw new TypeError(
864 `Piscēs: Tried to set property on object but [[Set]] returned false: ${P}`,
865 );
866 } else {
867 return O;
868 }
869 };
870
871 /**
872 * Sets the values of the enumerable own properties of the provided
873 * additional objects on the provided values.
874 *
875 * ※ This is effectively an alias for `Object.assign`.
876 */
877 export const setPropertyValues = (target, source, ...sources) => {
878 const to = toObject(target);
879 for (let i = -1; i < sources.length; ++i) {
880 // Iterate over each source and set the appropriate property
881 // values.
882 const nextSource = i === -1 ? source : sources[i];
883 if (nextSource != null) {
884 // The current source is not nullish; handle its own properties.
885 const from = toObject(nextSource);
886 const keys = ownKeys(from);
887 for (let k = 0; k < keys.length; ++k) {
888 // Iterate over each key in the current source and set it in
889 // the target object if it is enumerable.
890 const nextKey = keys[k];
891 const desc = reflectGetOwnPropertyDescriptor(from, nextKey);
892 if (desc !== UNDEFINED && desc.enumerable) {
893 // The current key is present and enumerable; set it to its
894 // corresponding value.
895 const propValue = from[nextKey];
896 to[nextKey] = propValue;
897 } else {
898 // The current key is not present or not enumerable.
899 /* do nothing */
900 }
901 }
902 } else {
903 // The current source is nullish.
904 /* do nothing */
905 }
906 }
907 return to;
908 };
909
910 /**
911 * Sets the prototype of the provided object to the provided value and
912 * returns the object.
913 *
914 * ※ This is effectively an alias for `Object.setPrototypeOf`, but it
915 * won’t throw when setting the prototype of a primitive to its current
916 * value.
917 */
918 export const setPrototype = (O, proto) => {
919 const obj = toObject(O);
920 if (O === obj) {
921 // The provided value is an object; set its prototype normally.
922 return setPrototypeOf(O, proto);
923 } else {
924 // The provided value is not an object; attempt to set the
925 // prototype on a coerced version with extensions prevented, then
926 // return the provided value.
927 //
928 // This will throw if the given prototype does not match the
929 // existing one on the coerced object.
930 setPrototypeOf(objectPreventExtensions(obj), proto);
931 return O;
932 }
933 };
934
935 /**
936 * Returns the provided value converted to an object.
937 *
938 * Existing objects are returned with no modification.
939 *
940 * ☡ This function throws if its argument is null or undefined.
941 */
942 export const toObject = ($) => {
943 if ($ == null) {
944 // The provided value is nullish; this is an error.
945 throw new TypeError(
946 `Piscēs: Cannot convert ${$} into an object.`,
947 );
948 } else {
949 // The provided value is not nullish; coerce it to an object.
950 return object($);
951 }
952 };
This page took 0.21628 seconds and 5 git commands to generate.