1 // ♓🌟 Piscēs ∷ object.js
2 // ====================================================================
4 // Copyright © 2022–2023 Lady [@ Lady’s Computer].
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/>.
22 const { isArray
} = Array
;
23 const object
= Object
;
32 getOwnPropertyDescriptor
: objectGetOwnPropertyDescriptor
,
34 getOwnPropertySymbols
: objectGetOwnPropertySymbols
,
41 preventExtensions
: objectPreventExtensions
,
50 getOwnPropertyDescriptor
: reflectGetOwnPropertyDescriptor
,
57 * An object whose properties are lazy‐loaded from the methods on the
58 * own properties of the provided object.
60 * This is useful when you are looking to reference properties on
61 * objects which, due to module dependency graphs, cannot be guaranteed
62 * to have been initialized yet.
64 * The resulting properties will have the same attributes (regarding
65 * configurability, enumerability, and writability) as the
66 * corresponding properties on the methods object. If a property is
67 * marked as writable, the method will never be called if it is set
68 * before it is gotten. By necessity, the resulting properties are all
69 * configurable before they are accessed for the first time.
71 * Methods will be called with the resulting object as their this
74 * `LazyLoader` objects have the same prototype as the passed methods
77 export class LazyLoader
extends null {
79 * Constructs a new `LazyLoader` object.
81 * ☡ This function throws if the provided value is not an object.
83 constructor(loadMethods
) {
84 if (type(loadMethods
) !== "object") {
85 // The provided value is not an object; throw an error.
87 `Piscēs: Cannot construct LazyLoader: Provided argument is not an object: ${loadMethods}.`,
90 // The provided value is an object; process it and build the
92 const result
= create(getPrototypeOf(loadMethods
));
93 const methodKeys
= ownKeys(loadMethods
);
94 for (let index
= 0; index
< methodKeys
.length
; ++index
) {
95 // Iterate over the property keys of the provided object and
96 // define getters and setters appropriately on the result.
97 const methodKey
= methodKeys
[index
];
98 const { configurable
, enumerable
, writable
} =
99 getOwnPropertyDescriptor(loadMethods
, methodKey
);
103 assign(create(null), {
108 const value
= call(loadMethods
[methodKey
], result
, []);
112 assign(create(null), {
122 assign(create(null), {
123 value
: toFunctionName(methodKey
, "get"),
132 assign(create(null), {
140 assign(create(null), {
141 value
: toFunctionName(methodKey
, "set"),
154 * Defines an own enumerable data property on the provided object with
155 * the provided property key and value.
157 export const defineOwnDataProperty
= (O
, P
, V
) =>
161 assign(create(null), {
170 * Defines an own nonenumerable data property on the provided object
171 * with the provided property key and value.
173 export const defineOwnNonenumerableDataProperty
= (O
, P
, V
) =>
177 assign(create(null), {
186 * Defines an own property on the provided object on the provided
187 * property key using the provided property descriptor.
189 * ※ This is effectively an alias for `Object.defineProperty`.
191 export const defineOwnProperty
= (O
, P
, Desc
) =>
192 defineProperty(O
, P
, Desc
);
195 * Defines own properties on the provided object using the descriptors
196 * on the enumerable own properties of the provided additional objects.
198 * ※ This differs from `Object.defineProperties` in that it can take
199 * multiple source objects.
201 export const defineOwnProperties
= (O
, ...sources
) => {
202 const { length
} = sources
;
203 for (let k
= 0; k
< length
; ++k
) {
204 defineProperties(O
, sources
[k
]);
210 * Removes the provided property key from the provided object and
211 * returns the object.
213 * ※ This function differs from `Reflect.deleteProperty` and the
214 * `delete` operator in that it throws if the deletion is
217 * ☡ This function throws if the first argument is not an object.
219 export const deleteOwnProperty
= (O
, P
) => {
220 if (type(O
) !== "object") {
222 `Piscēs: Tried to set property but provided value was not an object: ${V}`,
224 } else if (!deleteProperty(O
, P
)) {
226 `Piscēs: Tried to delete property from object but [[Delete]] returned false: ${P}`,
234 * Marks the provided object as non·extensible and marks all its
235 * properties as nonconfigurable and (if data properties) nonwritable,
236 * and returns the object.
238 * ※ This is effectively an alias for `Object.freeze`.
240 export const freeze
= (O
) => objectFreeze(O
);
243 * Returns a new frozen shallow copy of the enumerable own properties
244 * of the provided object, according to the following rules :—
246 * - For data properties, create a nonconfigurable, nonwritable
247 * property with the same value.
249 * - For accessor properties, create a nonconfigurable accessor
250 * property with the same getter *and* setter.
252 * The prototype for the resulting object will be taken from the
253 * `.prototype` property of the provided constructor, or the
254 * `.prototype` of the `.constructor` of the provided object if the
255 * provided constructor is undefined. If the used constructor has a
256 * nonnullish `.[Symbol.species]`, that will be used instead. If the
257 * used constructor or species is nullish or does not have a
258 * `.prototype` property, the prototype is set to null.
260 * ※ The prototype of the provided object itself is ignored.
262 export const frozenCopy
= (O
, constructor = O
?.constructor) => {
264 // O is null or undefined.
266 "Piscēs: Cannot copy properties of null or undefined.",
269 // O is not null or undefined.
271 // (If not provided, the constructor will be the value of getting
272 // the `.constructor` property of O.)
273 const species
= constructor?.[SPECIES
] ?? constructor;
275 species
== null || !("prototype" in species
)
279 const keys
= ownKeys(O
);
280 for (let k
= 0; k
< keys
.length
; ++k
) {
282 const Desc
= getOwnPropertyDescriptor(O
, P
);
283 if (Desc
.enumerable
) {
284 // P is an enumerable property.
290 isAccessorDescriptor(Desc
)
306 // P is not an enumerable property.
310 return objectPreventExtensions(copy
);
315 * Returns the function on the provided value at the provided property
318 * ☡ This function throws if the provided property key does not have an
319 * associated value which is callable.
321 export const getMethod
= (V
, P
) => {
325 } else if (typeof func
!== "function") {
326 throw new TypeError(`Piscēs: Method not callable: ${P}`);
333 * Returns the property descriptor record for the own property with the
334 * provided property key on the provided value, or null if none exists.
336 * ※ This is effectively an alias for
337 * `Object.getOwnPropertyDescriptor`, but the return value is a proxied
338 * object with null prototype.
340 export const getOwnPropertyDescriptor
= (O
, P
) => {
341 const desc
= objectGetOwnPropertyDescriptor(O
, P
);
342 return desc
=== UNDEFINED
? UNDEFINED
: toPropertyDescriptor(desc
);
346 * Returns the property descriptors for the own properties on the
349 * ※ This is effectively an alias for
350 * `Object.getOwnPropertyDescriptors`, but the values on the resulting
351 * object are proxied objects with null prototypes.
353 export const getOwnPropertyDescriptors
= (O
) => {
354 const obj
= toObject(O
);
355 const keys
= ownKeys(obj
);
356 const descriptors
= {};
357 for (let k
= 0; k
< keys
.length
; ++k
) {
359 defineOwnDataProperty(
362 getOwnPropertyDescriptor(O
, key
),
369 * Returns an array of property keys on the provided value.
371 * ※ This is effectively an alias for `Reflect.ownKeys`, except that
372 * it does not require that the argument be an object.
374 export const getOwnPropertyKeys
= (O
) => ownKeys(toObject(O
));
377 * Returns an array of string‐valued own property keys on the
380 * ☡ This includes both enumerable and non·enumerable properties.
382 * ※ This is effectively an alias for `Object.getOwnPropertyNames`.
384 export const getOwnPropertyStrings
= (O
) => getOwnPropertyNames(O
);
387 * Returns an array of symbol‐valued own property keys on the
390 * ☡ This includes both enumerable and non·enumerable properties.
392 * ※ This is effectively an alias for
393 * `Object.getOwnPropertySymbols`.
395 export const getOwnPropertySymbols
= (O
) =>
396 objectGetOwnPropertySymbols(O
);
399 * Returns the value of the provided property key on the provided
402 * ※ This is effectively an alias for `Reflect.get`, except that it
403 * does not require that the argument be an object.
405 export const getPropertyValue
= (O
, P
, Receiver
= O
) =>
406 get(toObject(O
), P
, Receiver
);
409 * Returns the prototype of the provided value.
411 * ※ This is effectively an alias for `Object.getPrototypeOf`.
413 export const getPrototype
= (O
) => getPrototypeOf(O
);
416 * Returns whether the provided value has an own property with the
417 * provided property key.
419 * ※ This is effectively an alias for `Object.hasOwn`.
421 export const hasOwnProperty
= (O
, P
) => hasOwn(O
, P
);
424 * Returns whether the provided property key exists on the provided
427 * ※ This is effectively an alias for `Reflect.has`, except that it
428 * does not require that the argument be an object.
430 * ※ This includes properties present on the prototype chain.
432 export const hasProperty
= (O
, P
) => has(toObject(O
), P
);
434 /** Returns whether the provided value is an arraylike object. */
435 export const isArraylikeObject
= ($) => {
436 if (type($) !== "object") {
440 lengthOfArraylike($); // throws if not arraylike
449 * Returns whether the provided value is spreadable during array
452 * This is also used to determine which things should be treated as
455 export const isConcatSpreadableObject
= ($) => {
456 if (type($) !== "object") {
457 // The provided value is not an object.
460 // The provided value is an object.
461 const spreadable
= $[IS_CONCAT_SPREADABLE
];
462 return spreadable
!== UNDEFINED
? !!spreadable
: isArray($);
467 * Returns whether the provided value is an extensible object.
469 * ※ This function returns false for nonobjects.
471 * ※ This is effectively an alias for `Object.isExtensible`.
473 export const isExtensibleObject
= (O
) => isExtensible(O
);
476 * Returns whether the provided value is an unfrozen object.
478 * ※ This function returns false for nonobjects.
480 * ※ This is effectively an alias for `!Object.isFrozen`.
482 export const isUnfrozenObject
= (O
) => !isFrozen(O
);
485 * Returns whether the provided value is an unsealed object.
487 * ※ This function returns false for nonobjects.
489 * ※ This is effectively an alias for `!Object.isSealed`.
491 export const isUnsealedObject
= (O
) => !isSealed(O
);
494 * Returns the length of the provided arraylike value.
496 * This can produce larger lengths than can actually be stored in
497 * arrays, because no such restrictions exist on arraylike methods.
499 * ☡ This function throws if the provided value is not arraylike.
501 export const lengthOfArraylike
= ({ length
}) => toLength(length
);
504 * Returns an array of key~value pairs for the enumerable,
505 * string‐valued property keys on the provided value.
507 * ※ This is effectively an alias for `Object.entries`.
509 export const namedEntries
= (O
) => entries(O
);
512 * Returns an array of the enumerable, string‐valued property keys on
513 * the provided value.
515 * ※ This is effectively an alias for `Object.keys`.
517 export const namedKeys
= (O
) => keys(O
);
520 * Returns an array of property values for the enumerable,
521 * string‐valued property keys on the provided value.
523 * ※ This is effectively an alias for `Object.values`.
525 export const namedValues
= (O
) => values(O
);
528 * Returns a new object with the provided prototype and property
531 * ※ This is effectively an alias for `Object.create`.
533 export const objectCreate
= (O
, Properties
) => create(O
, Properties
);
536 * Returns a new object with property keys and values from the provided
539 * ※ This is effectively an alias for `Object.fromEntries`.
541 export const objectFromEntries
= (iterable
) => fromEntries(iterable
);
544 * Marks the provided object as non·extensible, and returns the
547 * ※ This is effectively an alias for `Object.preventExtensions`.
549 export const preventExtensions
= (O
) => objectPreventExtensions(O
);
552 * Marks the provided object as non·extensible and marks all its
553 * properties as nonconfigurable, and returns the object.
555 * ※ This is effectively an alias for `Object.seal`.
557 export const seal
= (O
) => objectSeal(O
);
560 * Sets the provided property key to the provided value on the provided
561 * object and returns the object.
563 * ※ This function differs from `Reflect.set` in that it throws if the
564 * setting is unsuccessful.
566 * ☡ This function throws if the first argument is not an object.
568 export const setPropertyValue
= (O
, P
, V
, Receiver
= O
) => {
569 if (type(O
) !== "object") {
571 `Piscēs: Tried to set property but provided value was not an object: ${V}`,
573 } else if (!set(O
, P
, V
, Receiver
)) {
575 `Piscēs: Tried to set property on object but [[Set]] returned false: ${P}`,
583 * Sets the values of the enumerable own properties of the provided
584 * additional objects on the provided values.
586 * ※ This is effectively an alias for `Object.assign`.
588 export const setPropertyValues
= (target
, source
, ...sources
) => {
589 const to
= toObject(target
);
590 for (let i
= -1; i
< sources
.length
; ++i
) {
591 // Iterate over each source and set the appropriate property
593 const nextSource
= i
=== -1 ? source
: sources
[i
];
594 if (nextSource
!= null) {
595 // The current source is not nullish; handle its own properties.
596 const from = toObject(nextSource
);
597 const keys
= ownKeys(from);
598 for (let k
= 0; k
< keys
.length
; ++k
) {
599 // Iterate over each key in the current source and set it in
600 // the target object if it is enumerable.
601 const nextKey
= keys
[k
];
602 const desc
= reflectGetOwnPropertyDescriptor(from, nextKey
);
603 if (desc
!== UNDEFINED
&& desc
.enumerable
) {
604 // The current key is present and enumerable; set it to its
605 // corresponding value.
606 const propValue
= from[nextKey
];
607 to
[nextKey
] = propValue
;
609 // The current key is not present or not enumerable.
614 // The current source is nullish.
622 * Sets the prototype of the provided object to the provided value and
623 * returns the object.
625 * ※ This is effectively an alias for `Object.setPrototypeOf`, but it
626 * won’t throw when setting the prototype of a primitive to its current
629 export const setPrototype
= (O
, proto
) => {
630 const obj
= toObject(O
);
632 // The provided value is an object; set its prototype normally.
633 return setPrototypeOf(O
, proto
);
635 // The provided value is not an object; attempt to set the
636 // prototype on a coerced version with extensions prevented, then
637 // return the provided value.
639 // This will throw if the given prototype does not match the
640 // existing one on the coerced object.
641 setPrototypeOf(objectPreventExtensions(obj
), proto
);
647 * Returns the provided value converted to an object.
649 * Existing objects are returned with no modification.
651 * ☡ This function throws if its argument is null or undefined.
653 export const toObject
= ($) => {
655 // The provided value is nullish; this is an error.
657 `Piscēs: Cannot convert ${$} into an object.`,
660 // The provided value is not nullish; coerce it to an object.
666 * Returns the property key (symbol or string) corresponding to the
669 export const toPropertyKey
= ($) => {
670 const key
= toPrimitive($, "string");
671 return typeof key
=== "symbol" ? key
: `${key}`;