X-Git-Url: https://git.ladys.computer/Pisces/blobdiff_plain/8bf43f6e1898ca921d5e63b4513e4c6b2241ebc5..beab7268e7673b036222e64aac924f850e2b976e:/object.js diff --git a/object.js b/object.js index 3115c3b..ee3d51d 100644 --- a/object.js +++ b/object.js @@ -7,8 +7,20 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at . -import { bind, call, toFunctionName } from "./function.js"; -import { ITERATOR, SPECIES, toPrimitive, type } from "./value.js"; +import { + bind, + call, + createArrowFunction, + toFunctionName, +} from "./function.js"; +import { + IS_CONCAT_SPREADABLE, + ITERATOR, + SPECIES, + toLength, + toPrimitive, + type, +} from "./value.js"; /** * An object whose properties are lazy‐loaded from the methods on the @@ -402,15 +414,18 @@ export const { PropertyDescriptor } = (() => { return { PropertyDescriptor }; })(); -export const { - /** - * 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`. - */ - defineOwnProperty, +/** + * 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`. + */ +export const defineOwnProperty = createArrowFunction( + Object.defineProperty, + { name: "defineOwnProperty" }, +); +export const { /** * Defines own properties on the provided object using the * descriptors on the enumerable own properties of the provided @@ -422,77 +437,26 @@ export const { defineOwnProperties, /** - * Marks the provided object as non·extensible and marks all its - * properties as nonconfigurable and (if data properties) - * nonwritable, and returns the object. - * - * ※ This is effectively an alias for `Object.freeze`. - */ - freeze, - - /** - * Returns the property descriptor for the own property with the - * provided property key on the provided object, or null if none - * exists. - * - * ※ This is effectively an alias for - * `Object.getOwnPropertyDescriptor`. - */ - getOwnPropertyDescriptor, - - /** - * Returns the property descriptors for the own properties on the - * provided object. - * - * ※ This is effectively an alias for - * `Object.getOwnPropertyDescriptors`. - */ - getOwnPropertyDescriptors, - - /** - * Returns an array of string‐valued own property keys on the - * provided object. - * - * ☡ This includes both enumerable and non·enumerable properties. - * - * ※ This is effectively an alias for `Object.getOwnPropertyNames`. - */ - getOwnPropertyStrings, - - /** - * Returns an array of symbol‐valued own property keys on the - * provided object. - * - * ☡ This includes both enumerable and non·enumerable properties. - * - * ※ This is effectively an alias for - * `Object.getOwnPropertySymbols`. - */ - getOwnPropertySymbols, - - /** - * Returns the prototype of the provided object. + * Returns a new frozen shallow copy of the enumerable own properties + * of the provided object, according to the following rules :— * - * ※ This is effectively an alias for `Object.getPrototypeOf`. - */ - getPrototype, - - /** - * Returns whether the provided object has an own property with the - * provided property key. + * - For data properties, create a nonconfigurable, nonwritable + * property with the same value. * - * ※ This is effectively an alias for `Object.hasOwn`. - */ - hasOwnProperty, - - /** - * Returns whether the provided object is extensible. + * - For accessor properties, create a nonconfigurable accessor + * property with the same getter *and* setter. * - * ※ This function returns false for nonobjects. + * 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 + * provided constructor is undefined. If the used constructor has a + * 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. * - * ※ This is effectively an alias for `Object.isExtensible`. + * ※ The prototype of the provided object itself is ignored. */ - isExtensibleObject, + frozenCopy, /** * Returns whether the provided object is frozen. @@ -512,69 +476,6 @@ export const { */ isUnsealedObject, - /** - * Returns an array of key~value pairs for the enumerable, - * string‐valued property keys on the provided object. - * - * ※ This is effectively an alias for `Object.entries`. - */ - namedEntries, - - /** - * Returns an array of the enumerable, string‐valued property keys on - * the provided object. - * - * ※ This is effectively an alias for `Object.keys`. - */ - namedKeys, - - /** - * Returns an array of property values for the enumerable, - * string‐valued property keys on the provided object. - * - * ※ This is effectively an alias for `Object.values`. - */ - namedValues, - - /** - * Returns a new object with the provided prototype and property - * descriptors. - * - * ※ This is effectively an alias for `Object.create`. - */ - objectCreate, - - /** - * Returns a new object with the provided property keys and values. - * - * ※ This is effectively an alias for `Object.fromEntries`. - */ - objectFromEntries, - - /** - * Marks the provided object as non·extensible, and returns the - * object. - * - * ※ This is effectively an alias for `Object.preventExtensions`. - */ - preventExtensions, - - /** - * 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`. - */ - seal, - - /** - * Sets the values of the enumerable own properties of the provided - * additional objects on the provided object. - * - * ※ This is effectively an alias for `Object.assign`. - */ - setPropertyValues, - /** * Sets the prototype of the provided object to the provided value * and returns the object. @@ -594,72 +495,94 @@ export const { } = (() => { const createObject = Object; const { - assign, create, - defineProperty, defineProperties, - entries, - freeze, - fromEntries, - getOwnPropertyDescriptor, - getOwnPropertyDescriptors, - getOwnPropertyNames, - getOwnPropertySymbols, getPrototypeOf, - hasOwn, - isExtensible, isFrozen, isSealed, - keys, - preventExtensions, - seal, setPrototypeOf, - values, } = Object; - const { [ITERATOR]: arrayIterator } = Array.prototype; - const arrayIteratorNext = getPrototypeOf([][ITERATOR]()).next; - const splatIterablePrototype = { + const { + next: generatorIteratorNext, + } = getPrototypeOf(function* () {}.prototype); + const propertyDescriptorEntryIterablePrototype = { [ITERATOR]() { return { - next: bind( - arrayIteratorNext, - call(arrayIterator, this.args, []), - [], - ), + next: bind(generatorIteratorNext, this.generator(), []), }; }, }; - const splatIterable = ($) => - create(splatIterablePrototype, { args: { value: $ } }); + const propertyDescriptorEntryIterable = ($) => + create(propertyDescriptorEntryIterablePrototype, { + generator: { value: $ }, + }); return { - defineOwnProperty: (O, P, Attributes) => - defineProperty(O, P, Attributes), defineOwnProperties: (O, ...sources) => { - for (const source of splatIterable(sources)) { - defineProperties(O, source); + const { length } = sources; + for (let index = 0; index < length; ++index) { + defineProperties(O, sources[index]); } return O; }, - freeze: (O) => freeze(O), - getOwnPropertyDescriptor: (O, P) => getOwnPropertyDescriptor(O, P), - getOwnPropertyDescriptors: (O) => getOwnPropertyDescriptors(O), - getOwnPropertyStrings: (O) => getOwnPropertyNames(O), - getOwnPropertySymbols: (O) => getOwnPropertySymbols(O), - getPrototype: (O) => getPrototypeOf(O), - hasOwnProperty: (O, P) => hasOwn(O, P), - isExtensibleObject: (O) => isExtensible(O), + frozenCopy: (O, constructor = O?.constructor) => { + if (O == null) { + // O is null or undefined. + throw new TypeError( + "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.) + const species = constructor?.[SPECIES] ?? constructor; + return preventExtensions( + objectCreate( + species == null || !("prototype" in species) + ? null + : species.prototype, + objectFromEntries( + propertyDescriptorEntryIterable(function* () { + const ownPropertyKeys = getOwnPropertyKeys(O); + for ( + let i = 0; + i < ownPropertyKeys.length; + ++i + ) { + const P = ownPropertyKeys[i]; + const Desc = getOwnPropertyDescriptor(O, P); + if (Desc.enumerable) { + // P is an enumerable property. + yield [ + P, + "get" in Desc || "set" in Desc + ? { + configurable: false, + enumerable: true, + get: Desc.get, + set: Desc.set, + } + : { + configurable: false, + enumerable: true, + value: Desc.value, + writable: false, + }, + ]; + } else { + // P is not an enumerable property. + /* do nothing */ + } + } + }), + ), + ), + ); + } + }, isUnfrozenObject: (O) => !isFrozen(O), isUnsealedObject: (O) => !isSealed(O), - namedEntries: (O) => entries(O), - namedKeys: (O) => keys(O), - namedValues: (O) => values(O), - objectCreate: (O, Properties) => create(O, Properties), - objectFromEntries: (iterable) => fromEntries(iterable), - preventExtensions: (O) => preventExtensions(O), - seal: (O) => seal(O), - setPropertyValues: (target, source, ...sources) => - assign(target, source, ...splatIterable(sources)), setPrototype: (O, proto) => { const obj = toObject(O); if (O === obj) { @@ -778,105 +701,14 @@ export const { }; })(); -export const { - /** - * Returns a new frozen shallow copy of the enumerable own properties - * of the provided object, according to the following rules :— - * - * - For data properties, create a nonconfigurable, nonwritable - * property with the same value. - * - * - For accessor properties, create a nonconfigurable accessor - * 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 - * provided constructor is undefined. If the used constructor has a - * 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. - * - * ※ The prototype of the provided object itself is ignored. - */ - frozenCopy, -} = (() => { - const { - next: generatorIteratorNext, - } = getPrototype(function* () {}.prototype); - const propertyDescriptorEntryIterablePrototype = { - [ITERATOR]() { - return { - next: bind(generatorIteratorNext, this.generator(), []), - }; - }, - }; - return { - frozenCopy: (O, constructor = O?.constructor) => { - if (O == null) { - // O is null or undefined. - throw new TypeError( - "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.) - const species = constructor?.[SPECIES] ?? constructor; - return preventExtensions( - objectCreate( - species == null || !("prototype" in species) - ? null - : species.prototype, - objectFromEntries( - objectCreate( - propertyDescriptorEntryIterablePrototype, - { - generator: { - value: function* () { - const ownPropertyKeys = getOwnPropertyKeys(O); - for ( - let i = 0; - i < ownPropertyKeys.length; - ++i - ) { - const P = ownPropertyKeys[i]; - const Desc = getOwnPropertyDescriptor(O, P); - if (Desc.enumerable) { - // P is an enumerable property. - yield [ - P, - "get" in Desc || "set" in Desc - ? { - configurable: false, - enumerable: true, - get: Desc.get, - set: Desc.set, - } - : { - configurable: false, - enumerable: true, - value: Desc.value, - writable: false, - }, - ]; - } else { - // P is not an enumerable property. - /* do nothing */ - } - } - }, - }, - }, - ), - ), - ), - ); - } - }, - }; -})(); +/** + * Marks the provided object as non·extensible and marks all its + * properties as nonconfigurable and (if data properties) nonwritable, + * and returns the object. + * + * ※ This is effectively an alias for `Object.freeze`. + */ +export const freeze = createArrowFunction(Object.freeze); /** * Returns the function on the provided value at the provided property @@ -896,6 +728,215 @@ export const getMethod = (V, P) => { } }; +/** + * Returns the property descriptor for the own property with the + * provided property key on the provided object, or null if none + * exists. + * + * ※ This is effectively an alias for + * `Object.getOwnPropertyDescriptor`. + */ +export const getOwnPropertyDescriptor = createArrowFunction( + Object.getOwnPropertyDescriptor, +); + +/** + * Returns the property descriptors for the own properties on the + * provided object. + * + * ※ This is effectively an alias for + * `Object.getOwnPropertyDescriptors`. + */ +export const getOwnPropertyDescriptors = createArrowFunction( + Object.getOwnPropertyDescriptors, +); + +/** + * Returns an array of string‐valued own property keys on the + * provided object. + * + * ☡ This includes both enumerable and non·enumerable properties. + * + * ※ This is effectively an alias for `Object.getOwnPropertyNames`. + */ +export const getOwnPropertyStrings = createArrowFunction( + Object.getOwnPropertyNames, + { name: "getOwnPropertyStrings" }, +); + +/** + * Returns an array of symbol‐valued own property keys on the + * provided object. + * + * ☡ This includes both enumerable and non·enumerable properties. + * + * ※ This is effectively an alias for + * `Object.getOwnPropertySymbols`. + */ +export const getOwnPropertySymbols = createArrowFunction( + Object.getOwnPropertySymbols, +); + +/** + * Returns the prototype of the provided object. + * + * ※ This is effectively an alias for `Object.getPrototypeOf`. + */ +export const getPrototype = createArrowFunction( + Object.getPrototypeOf, + { name: "getPrototype" }, +); + +/** + * Returns whether the provided object has an own property with the + * provided property key. + * + * ※ This is effectively an alias for `Object.hasOwn`. + */ +export const hasOwnProperty = createArrowFunction(Object.hasOwn, { + name: "hasOwnProperty", +}); + +/** Returns whether the provided value is an arraylike object. */ +export const isArraylikeObject = ($) => { + if (type($) !== "object") { + return false; + } else { + try { + lengthOfArraylike($); // throws if not arraylike + return true; + } catch { + return false; + } + } +}; + +export const { + /** + * Returns whether the provided value is spreadable during array + * concatenation. + * + * This is also used to determine which things should be treated as + * collections. + */ + isConcatSpreadableObject, +} = (() => { + const { isArray } = Array; + + return { + isConcatSpreadableObject: ($) => { + if (type($) !== "object") { + // The provided value is not an object. + return false; + } else { + // The provided value is an object. + const spreadable = $[IS_CONCAT_SPREADABLE]; + return spreadable !== undefined ? !!spreadable : isArray($); + } + }, + }; +})(); + +/** + * Returns whether the provided object is extensible. + * + * ※ This function returns false for nonobjects. + * + * ※ This is effectively an alias for `Object.isExtensible`. + */ +export const isExtensibleObject = createArrowFunction( + Object.isExtensible, + { name: "isExtensibleObject" }, +); + +/** + * Returns the length of the provided arraylike value. + * + * This can produce larger lengths than can actually be stored in + * arrays, because no such restrictions exist on arraylike methods. + * + * ☡ This function throws if the provided value is not arraylike. + */ +export const lengthOfArraylike = ({ length }) => toLength(length); + +/** + * Returns an array of key~value pairs for the enumerable, + * string‐valued property keys on the provided object. + * + * ※ This is effectively an alias for `Object.entries`. + */ +export const namedEntries = createArrowFunction(Object.entries, { + name: "namedEntries", +}); + +/** + * Returns an array of the enumerable, string‐valued property keys on + * the provided object. + * + * ※ This is effectively an alias for `Object.keys`. + */ +export const namedKeys = createArrowFunction(Object.keys, { + name: "namedKeys", +}); + +/** + * Returns an array of property values for the enumerable, + * string‐valued property keys on the provided object. + * + * ※ This is effectively an alias for `Object.values`. + */ +export const namedValues = createArrowFunction(Object.values, { + name: "namedValues", +}); + +/** + * Returns a new object with the provided prototype and property + * descriptors. + * + * ※ This is effectively an alias for `Object.create`. + */ +export const objectCreate = createArrowFunction(Object.create, { + name: "objectCreate", +}); + +/** + * Returns a new object with the provided property keys and values. + * + * ※ This is effectively an alias for `Object.fromEntries`. + */ +export const objectFromEntries = createArrowFunction( + Object.fromEntries, + { name: "objectFromEntries" }, +); + +/** + * Marks the provided object as non·extensible, and returns the + * object. + * + * ※ This is effectively an alias for `Object.preventExtensions`. + */ +export const preventExtensions = createArrowFunction( + Object.preventExtensions, +); + +/** + * 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`. + */ +export const seal = createArrowFunction(Object.seal); + +/** + * Sets the values of the enumerable own properties of the provided + * additional objects on the provided object. + * + * ※ This is effectively an alias for `Object.assign`. + */ +export const setPropertyValues = createArrowFunction(Object.assign, { + name: "setPropertyValues", +}); + /** * Returns the property key (symbol or string) corresponding to the * provided value.