-// ♓🌟 Piscēs ∷ object.js
-// ====================================================================
-//
-// Copyright © 2022–2023 Lady [@ Lady’s Computer].
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at <https://mozilla.org/MPL/2.0/>.
+// SPDX-FileCopyrightText: 2022, 2023, 2025 Lady <https://www.ladys.computer/about/#lady>
+// SPDX-License-Identifier: MPL-2.0
+/**
+ * ⁌ ♓🧩 Piscēs ∷ object.js
+ *
+ * Copyright © 2022–2023, 2025 Lady [@ Ladys Computer].
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at <https://mozilla.org/MPL/2.0/>.
+ */
import {
IS_CONCAT_SPREADABLE,
UNDEFINED,
} from "./value.js";
+const PISCĒS = "♓🧩 Piscēs";
+
const createArray = Array;
const { isArray } = Array;
const object = Object;
* Methods will be called with the resulting object as their this
* value.
*
- * `LazyLoader` objects have the same prototype as the passed methods
+ * `LazyLoader´ objects have the same prototype as the passed methods
* object.
*/
export class LazyLoader extends null {
/**
- * Constructs a new `LazyLoader` object.
+ * Constructs a new `LazyLoader´ object.
*
* ☡ This function throws if the provided value is not an object.
*/
constructor(loadMethods) {
if (type(loadMethods) !== "object") {
- // The provided value is not an object; throw an error.
+ // The provided value is not an object; this is an error.
throw new TypeError(
- `Piscēs: Cannot construct LazyLoader: Provided argument is not an object: ${loadMethods}.`,
+ `${PISCĒS}: Cannot construct LazyLoader: Provided argument is not an object: ${loadMethods}.`,
);
} else {
- // The provided value is an object; process it and build the
- // result.
+ // The provided value is an object.
+ //
+ // Process it and build the result.
const result = create(getPrototypeOf(loadMethods));
const methodKeys = ownKeys(loadMethods);
for (let index = 0; index < methodKeys.length; ++index) {
* Defines an own property on the provided object on the provided
* property key using the provided property descriptor.
*
- * ※ This is effectively an alias for `Object.defineProperty`.
+ * ※ This is effectively an alias for `Object.defineProperty´.
*/
export const defineOwnProperty = (O, P, Desc) =>
defineProperty(O, P, Desc);
* Defines own properties on the provided object using the descriptors
* on the enumerable own properties of the provided additional objects.
*
- * ※ This differs from `Object.defineProperties` in that it can take
- * multiple source objects.
+ * ※ This function differs from `Object.defineProperties´ in that it
+ * can take multiple source objects.
*/
export const defineOwnProperties = (O, ...sources) => {
const { length } = sources;
for (let k = 0; k < length; ++k) {
+ // Iterate over each source and define the appropriate properties
+ // on the provided object.
defineProperties(O, sources[k]);
}
return O;
* Removes the provided property key from the provided object and
* returns the object.
*
- * ※ This function differs from `Reflect.deleteProperty` and the
- * `delete` operator in that it throws if the deletion is
+ * ※ This function differs from `Reflect.deleteProperty´ and the
+ * `delete´ operator in that it throws if the deletion is
* unsuccessful.
*
* ☡ This function throws if the first argument is not an object.
*/
export const deleteOwnProperty = (O, P) => {
if (type(O) !== "object") {
+ // The provided value is not an object; this is an error.
throw new TypeError(
- `Piscēs: Tried to set property but provided value was not an object: ${V}`,
+ `${PISCĒS}: Tried to set property but provided value was not an object: ${V}`,
);
} else if (!deleteProperty(O, P)) {
+ // The provided property could not be deleted on the provided
+ // value; this is an error.
throw new TypeError(
- `Piscēs: Tried to delete property from object but [[Delete]] returned false: ${P}`,
+ `${PISCĒS}: Tried to delete property from object but [[Delete]] returned false: ${P}`,
);
} else {
+ // The provided property was successfully deleted.
+ //
+ // Return the provided value.
return O;
}
};
* properties as nonconfigurable and (if data properties) nonwritable,
* and returns the object.
*
- * ※ This is effectively an alias for `Object.freeze`.
+ * ※ This is effectively an alias for `Object.freeze´.
*/
export const freeze = (O) => objectFreeze(O);
* property with the same value.
*
* - For accessor properties, create a nonconfigurable accessor
- * property with the same getter *and* setter.
+ * property with the same getter ⹐and⹑ setter.
*
* The prototype for the resulting object will be taken from the
- * `.prototype` property of the provided constructor, or the
- * `.prototype` of the `.constructor` of the provided object if the
+ * `.prototype´ property of the provided constructor, or the
+ * `.prototype´ of the `.constructor´ of the provided object if the
* provided constructor is undefined. If the used constructor has a
- * nonnullish `.[Symbol.species]`, that will be used instead. If the
+ * nonnullish `.[Symbol.species]´, that will be used instead. If the
* used constructor or species is nullish or does not have a
- * `.prototype` property, the prototype is set to null.
+ * `.prototype´ property, the prototype is set to null.
*
* ※ The prototype of the provided object itself is ignored.
*/
if (O == null) {
// O is null or undefined.
throw new TypeError(
- "Piscēs: Cannot copy properties of null or undefined.",
+ `${PISCĒS}: Cannot copy properties of null or undefined.`,
);
} else {
// O is not null or undefined.
//
// (If not provided, the constructor will be the value of getting
- // the `.constructor` property of O.)
+ // the `.constructor´ property of O.)
const species = constructor?.[SPECIES] ?? constructor;
const copy = create(
species == null || !("prototype" in species)
);
const keys = ownKeys(O);
for (let k = 0; k < keys.length; ++k) {
+ // Iterate over each key and define the appropriate value on the
+ // result.
const P = keys[k];
const Desc = getOwnPropertyDescriptor(O, P);
if (Desc.enumerable) {
* key.
*
* ☡ This function throws if the provided property key does not have an
- * associated value which is callable.
+ * associated value which is either nullish or callable.
*/
export const getMethod = (V, P) => {
const func = V[P];
if (func == null) {
- return undefined;
+ // The value of the provided property is nullish.
+ //
+ // Return undefined.
+ return UNDEFINED;
} else if (typeof func !== "function") {
- throw new TypeError(`Piscēs: Method not callable: ${P}`);
+ // The value of the provided property is not callable; this is an
+ // error.
+ throw new TypeError(`${PISCĒS}: Method not callable: ${P}`);
} else {
+ // The value of the provided property is callable.
+ //
+ // Return it.
return func;
}
};
* provided property key on the provided value, or null if none exists.
*
* ※ This is effectively an alias for
- * `Object.getOwnPropertyDescriptor`, but the return value is a proxied
+ * `Object.getOwnPropertyDescriptor´, but the return value is a proxied
* object with null prototype.
*/
export const getOwnPropertyDescriptor = (O, P) => {
* provided value.
*
* ※ This is effectively an alias for
- * `Object.getOwnPropertyDescriptors`, but the values on the resulting
+ * `Object.getOwnPropertyDescriptors´, but the values on the resulting
* object are proxied objects with null prototypes.
*/
export const getOwnPropertyDescriptors = (O) => {
/**
* Returns an array of own property keys on the provided value.
*
- * ※ This is effectively an alias for `Reflect.ownKeys`, except that
+ * ※ This is effectively an alias for `Reflect.ownKeys´, except that
* it does not require that the argument be an object.
*/
export const getOwnPropertyKeys = (O) => ownKeys(toObject(O));
*
* ☡ This includes both enumerable and non·enumerable properties.
*
- * ※ This is effectively an alias for `Object.getOwnPropertyNames`.
+ * ※ This is effectively an alias for `Object.getOwnPropertyNames´.
*/
export const getOwnPropertyStrings = (O) => getOwnPropertyNames(O);
* ☡ This includes both enumerable and non·enumerable properties.
*
* ※ This is effectively an alias for
- * `Object.getOwnPropertySymbols`.
+ * `Object.getOwnPropertySymbols´.
*/
export const getOwnPropertySymbols = (O) =>
objectGetOwnPropertySymbols(O);
const desc = getOwnPropertyDescriptor(O, P);
if (desc === UNDEFINED) {
// The provided property is not an own property on the provided
- // value; return undefined.
+ // value.
+ //
+ // Return undefined.
return UNDEFINED;
}
if (isDataDescriptor(desc)) {
- // The provided property is a data property; return its value.
+ // The provided property is a data property.
+ //
+ // Return its value.
return desc.value;
} else {
- // The provided property is an accessor property; get its value
- // using the appropriate receiver.
+ // The provided property is an accessor property.
+ //
+ // Get its value using the appropriate receiver.
const target = Receiver === UNDEFINED ? obj : toObject(Receiver);
return call(desc.get, target, []);
}
* Returns the value of the provided property key on the provided
* value.
*
- * ※ This is effectively an alias for `Reflect.get`, except that it
+ * ※ This is effectively an alias for `Reflect.get´, except that it
* does not require that the argument be an object.
*/
export const getPropertyValue = (O, P, Receiver = O) =>
/**
* Returns the prototype of the provided value.
*
- * ※ This is effectively an alias for `Object.getPrototypeOf`.
+ * ※ This is effectively an alias for `Object.getPrototypeOf´.
*/
export const getPrototype = (O) => getPrototypeOf(O);
* Returns whether the provided value has an own property with the
* provided property key.
*
- * ※ This is effectively an alias for `Object.hasOwn`.
+ * ※ This is effectively an alias for `Object.hasOwn´.
*/
export const hasOwnProperty = (O, P) => hasOwn(O, P);
* Returns whether the provided property key exists on the provided
* value.
*
- * ※ This is effectively an alias for `Reflect.has`, except that it
+ * ※ This is effectively an alias for `Reflect.has´, except that it
* does not require that the argument be an object.
*
* ※ This includes properties present on the prototype chain.
/** Returns whether the provided value is an arraylike object. */
export const isArraylikeObject = ($) => {
if (type($) !== "object") {
+ // The provided value is not an object.
return false;
} else {
+ // The provided value is an object.
try {
- lengthOfArraylike($); // throws if not arraylike
+ // Try to get the length and return true.
+ //
+ // ※ If this throws, the object is not arraylike.
+ lengthOfArraylike($);
return true;
} catch {
+ // Getting the length failed; return false.
return false;
}
}
*
* ※ This function returns false for nonobjects.
*
- * ※ This is effectively an alias for `Object.isExtensible`.
+ * ※ This is effectively an alias for `Object.isExtensible´.
*/
export const isExtensibleObject = (O) => isExtensible(O);
export const {
/**
* Returns whether the provided value is a property descriptor record
- * as created by `toPropertyDescriptor`.
+ * as created by `toPropertyDescriptor´.
*
* ※ This function is provided to enable inspection of whether an
- * object uses the property descriptor record proxy implementation,
- * not as a general test of whether an object satisfies the
- * requirements for property descriptors. In most cases, a more
- * specific test, like `isAccessorDescriptor`, `isDataDescriptor`, or
- * `isGenericDescriptor`, is preferrable.
+ * object uses the ♓🧩 Piscēs property descriptor record proxy
+ * implementation, not as a general test of whether an object
+ * satisfies the requirements for property descriptors. In most
+ * cases, a more specific test, like `isAccessorDescriptor´,
+ * `isDataDescriptor´, or `isGenericDescriptor´, is preferrable.
*/
isPropertyDescriptorRecord,
/**
* Converts the provided value to a property descriptor record.
*
- * ※ The prototype of a property descriptor record is always `null`.
+ * ※ The prototype of a property descriptor record is always `null´.
*
* ※ Actually constructing a property descriptor object using this
* class is only necessary if you need strict guarantees about the
* types of its properties; the resulting object is proxied to ensure
* the types match what one would expect from composing
- * FromPropertyDescriptor and ToPropertyDescriptor in the Ecmascript
- * specification.
+ * `FromPropertyDescriptor´ and `ToPropertyDescriptor´ in the
+ * Ecmascript specification.
*/
toPropertyDescriptorRecord,
} = (() => {
case "value":
return V;
case "get":
- if (V !== undefined && typeof V !== "function") {
+ if (V !== UNDEFINED && typeof V !== "function") {
+ // The provided value is not callable; this is an error.
throw new TypeError(
- "Piscēs: Getters must be callable.",
+ `${PISCĒS}: Getters must be callable.`,
);
} else {
+ // The provided value is callable.
return V;
}
case "set":
- if (V !== undefined && typeof V !== "function") {
+ if (V !== UNDEFINED && typeof V !== "function") {
+ // The provided value is not callable; this is an error.
throw new TypeError(
- "Piscēs: Setters must be callable.",
+ `${PISCĒS}: Setters must be callable.`,
);
} else {
+ // The provided value is callable.
return V;
}
default:
{
defineProperty(O, P, Desc) {
if (
- P === "configurable" || P === "enumerable" ||
- P === "writable" || P === "value" ||
- P === "get" || P === "set"
+ P === "configurable" || P === "enumerable"
+ || P === "writable" || P === "value"
+ || P === "get" || P === "set"
) {
- // P is a property descriptor attribute.
+ // `P´ is a property descriptor attribute.
const desc = assign(objectCreate(null), Desc);
if ("get" in desc || "set" in desc) {
- // Desc is an accessor property descriptor.
+ // `Desc´ is an accessor property descriptor.
throw new TypeError(
- "Piscēs: Property descriptor attributes must be data properties.",
+ `${PISCĒS}: Property descriptor attributes must be data properties.`,
);
} else if ("value" in desc || !(P in O)) {
- // Desc has a value or P does not already exist on O.
+ // `Desc´ has a value or `P´ does not already exist on
+ // `O´.
desc.value = coercePropertyDescriptorValue(
P,
desc.value,
);
} else {
- // Desc is not an accessor property descriptor and has no
- // value, but an existing value is present on O.
+ // `Desc´ is not an accessor property descriptor and has
+ // no value, but an existing value is present on `O´.
/* do nothing */
}
- const isAccessorDescriptor = "get" === P || "set" === P ||
- "get" in O || "set" in O;
- const isDataDescriptor = "value" === P ||
- "writable" === P ||
- "value" in O || "writable" in O;
+ const isAccessorDescriptor = "get" === P || "set" === P
+ || "get" in O || "set" in O;
+ const isDataDescriptor = "value" === P
+ || "writable" === P
+ || "value" in O || "writable" in O;
if (isAccessorDescriptor && isDataDescriptor) {
- // Both accessor and data attributes will be present on O
- // after defining P.
+ // Both accessor and data attributes will be present on
+ // `O´ after defining `P´; this is an error.
throw new TypeError(
- "Piscēs: Property descriptors cannot specify both accessor and data attributes.",
+ `${PISCĒS}: Property descriptors cannot specify both accessor and data attributes.`,
);
} else {
- // P can be safely defined on O.
+ // `P´ can be safely defined on `O´.
return reflectDefineProperty(O, P, desc);
}
} else {
- // P is not a property descriptor attribute.
+ // `P´ is not a property descriptor attribute.
return reflectDefineProperty(O, P, Desc);
}
},
setPrototypeOf(O, V) {
if (V !== null) {
- // V is not the property descriptor prototype.
+ // `V´ is not the property descriptor prototype.
return false;
} else {
- // V is the property descriptor prototype.
+ // `V´ is the property descriptor prototype.
return reflectSetPrototypeOf(O, V);
}
},
call(weakSetHas, propertyDescriptorRecords, [$]),
toPropertyDescriptorRecord: (Obj) => {
if (type(Obj) !== "object") {
- // The provided value is not an object.
+ // The provided value is not an object; this is an error.
throw new TypeError(
- `Piscēs: Cannot convert primitive to property descriptor: ${O}.`,
+ `${PISCĒS}: Cannot convert primitive to property descriptor: ${O}.`,
);
} else {
// The provided value is an object.
// A get property is specified.
const getter = Obj.get;
if (getter !== UNDEFINED && typeof getter !== "function") {
- // The getter is not callable.
- throw new TypeError("Piscēs: Getters must be callable.");
+ // The getter is not callable; this is an error.
+ throw new TypeError(
+ `${PISCĒS}: Getters must be callable.`,
+ );
} else {
// The getter is callable.
defineOwnDataProperty(desc, "get", Obj.get);
// A set property is specified.
const setter = Obj.set;
if (setter !== UNDEFINED && typeof setter !== "function") {
- // The setter is not callable.
- throw new TypeError("Piscēs: Setters must be callable.");
+ // The setter is not callable; this is an error.
+ throw new TypeError(
+ `${PISCĒS}: Setters must be callable.`,
+ );
} else {
// The setter is callable.
defineOwnDataProperty(desc, "set", Obj.set);
/* do nothing */
}
if (
- ("get" in desc || "set" in desc) &&
- ("value" in desc || "writable" in desc)
+ ("get" in desc || "set" in desc)
+ && ("value" in desc || "writable" in desc)
) {
- // Both accessor and data attributes have been defined.
+ // Both accessor and data attributes have been defined; this
+ // is an error.
throw new TypeError(
- "Piscēs: Property descriptors cannot specify both accessor and data attributes.",
+ `${PISCĒS}: Property descriptors cannot specify both accessor and data attributes.`,
);
} else {
// The property descriptor is valid.
*
* ※ This function returns false for nonobjects.
*
- * ※ This is effectively an alias for `!Object.isFrozen`.
+ * ※ This is effectively an alias for `!Object.isFrozen´.
*/
export const isUnfrozenObject = (O) => !isFrozen(O);
*
* ※ This function returns false for nonobjects.
*
- * ※ This is effectively an alias for `!Object.isSealed`.
+ * ※ This is effectively an alias for `!Object.isSealed´.
*/
export const isUnsealedObject = (O) => !isSealed(O);
* Returns an array of key~value pairs for the enumerable,
* string‐valued property keys on the provided value.
*
- * ※ This is effectively an alias for `Object.entries`.
+ * ※ This is effectively an alias for `Object.entries´.
*/
export const namedEntries = (O) => entries(O);
* Returns an array of the enumerable, string‐valued property keys on
* the provided value.
*
- * ※ This is effectively an alias for `Object.keys`.
+ * ※ This is effectively an alias for `Object.keys´.
*/
export const namedKeys = (O) => keys(O);
* Returns an array of property values for the enumerable,
* string‐valued property keys on the provided value.
*
- * ※ This is effectively an alias for `Object.values`.
+ * ※ This is effectively an alias for `Object.values´.
*/
export const namedValues = (O) => values(O);
* Returns a new object with the provided prototype and property
* descriptors.
*
- * ※ This is effectively an alias for `Object.create`.
+ * ※ This is effectively an alias for `Object.create´.
*/
export const objectCreate = (O, Properties) => create(O, Properties);
* Returns a new object with property keys and values from the provided
* iterable value.
*
- * ※ This is effectively an alias for `Object.fromEntries`.
+ * ※ This is effectively an alias for `Object.fromEntries´.
*/
export const objectFromEntries = (iterable) => fromEntries(iterable);
* Marks the provided object as non·extensible, and returns the
* object.
*
- * ※ This is effectively an alias for `Object.preventExtensions`.
+ * ※ This is effectively an alias for `Object.preventExtensions´.
*/
export const preventExtensions = (O) => objectPreventExtensions(O);
* Marks the provided object as non·extensible and marks all its
* properties as nonconfigurable, and returns the object.
*
- * ※ This is effectively an alias for `Object.seal`.
+ * ※ This is effectively an alias for `Object.seal´.
*/
export const seal = (O) => objectSeal(O);
* Sets the provided property key to the provided value on the provided
* object and returns the object.
*
- * ※ This function differs from `Reflect.set` in that it throws if the
+ * ※ This function differs from `Reflect.set´ in that it throws if the
* setting is unsuccessful.
*
* ☡ This function throws if the first argument is not an object.
*/
export const setPropertyValue = (O, P, V, Receiver = O) => {
if (type(O) !== "object") {
+ // The provided value is not an object; this is an error.
throw new TypeError(
- `Piscēs: Tried to set property but provided value was not an object: ${V}`,
+ `${PISCĒS}: Tried to set property but provided value was not an object: ${V}`,
);
} else if (!set(O, P, V, Receiver)) {
+ // Setting the property fails; this is an error.
throw new TypeError(
- `Piscēs: Tried to set property on object but [[Set]] returned false: ${P}`,
+ `${PISCĒS}: Tried to set property on object but [[Set]] returned false: ${P}`,
);
} else {
+ // The property was successfully set.
+ //
+ // Return the provided object.
return O;
}
};
* Sets the values of the enumerable own properties of the provided
* additional objects on the provided values.
*
- * ※ This is effectively an alias for `Object.assign`.
+ * ※ This is effectively an alias for `Object.assign´.
*/
export const setPropertyValues = (target, source, ...sources) => {
const to = toObject(target);
// values.
const nextSource = i === -1 ? source : sources[i];
if (nextSource != null) {
- // The current source is not nullish; handle its own properties.
+ // The current source is not nullish.
+ //
+ // Handle its own properties.
const from = toObject(nextSource);
const keys = ownKeys(from);
for (let k = 0; k < keys.length; ++k) {
const nextKey = keys[k];
const desc = reflectGetOwnPropertyDescriptor(from, nextKey);
if (desc !== UNDEFINED && desc.enumerable) {
- // The current key is present and enumerable; set it to its
- // corresponding value.
+ // The current key is present and enumerable.
+ //
+ // Set it to its corresponding value.
const propValue = from[nextKey];
to[nextKey] = propValue;
} else {
* Sets the prototype of the provided object to the provided value and
* returns the object.
*
- * ※ This is effectively an alias for `Object.setPrototypeOf`, but it
- * won’t throw when setting the prototype of a primitive to its current
+ * ※ This is effectively an alias for `Object.setPrototypeOf´, but it
+ * won¦t throw when setting the prototype of a primitive to its current
* value.
*/
export const setPrototype = (O, proto) => {
const obj = toObject(O);
if (O === obj) {
- // The provided value is an object; set its prototype normally.
+ // The provided value is an object.
+ //
+ // Set its prototype normally.
return setPrototypeOf(O, proto);
} else {
- // The provided value is not an object; attempt to set the
- // prototype on a coerced version with extensions prevented, then
- // return the provided value.
+ // The provided value is not an object.
//
- // This will throw if the given prototype does not match the
+ // Attempt to set the prototype on a coerced version with
+ // extensions prevented, then return the provided value.
+ //
+ // ☡ This will throw if the given prototype does not match the
// existing one on the coerced object.
setPrototypeOf(objectPreventExtensions(obj), proto);
return O;
if ($ == null) {
// The provided value is nullish; this is an error.
throw new TypeError(
- `Piscēs: Cannot convert ${$} into an object.`,
+ `${PISCĒS}: Cannot convert ${$} into an object.`,
);
} else {
- // The provided value is not nullish; coerce it to an object.
+ // The provided value is not nullish.
+ //
+ // Coerce it to an object.
return object($);
}
};