From: Lady Date: Sat, 9 Jul 2022 23:02:22 +0000 (-0700) Subject: Basic functions for object & function manipulation X-Git-Tag: 0.1.0~9 X-Git-Url: https://git.ladys.computer/Pisces/commitdiff_plain/6f1ff895670d04034ef09faea4779923a85097fb?ds=sidebyside;hp=15a0a4d62a2816fc55f98064a81034ae2fc13404 Basic functions for object & function manipulation --- diff --git a/base64.js b/base64.js index 707e8e6..ff58328 100644 --- a/base64.js +++ b/base64.js @@ -7,6 +7,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at . +import { getPrototype, hasOwnProperty } from "./object.js"; + /** * Returns an ArrayBuffer generated from the provided Base64. * @@ -17,7 +19,7 @@ export const a2b = ($, ...$s) => { const source = ( typeof $ == "string" ? $ - : Object.hasOwn($, "raw") + : hasOwnProperty($, "raw") ? String.raw($, ...$s) : `${$}` ).replace(/[\t\n\f\r ]+/gu, ""); @@ -82,8 +84,7 @@ export const a2b = ($, ...$s) => { export const b2a = ($, ...$s) => { const buffer = $ instanceof ArrayBuffer ? $ - : $ instanceof DataView || - $ instanceof Object.getPrototypeOf(Uint8Array) + : $ instanceof DataView || $ instanceof getPrototype(Uint8Array) ? $.buffer : ((string) => Array.prototype.reduce.call( diff --git a/collection.js b/collection.js index 6920661..10172e6 100644 --- a/collection.js +++ b/collection.js @@ -8,7 +8,7 @@ // file, You can obtain one at . import { MAX_SAFE_INTEGER } from "./numeric.js"; -import { isObject } from "./object.js"; +import { isObject, sameValue } from "./object.js"; /** * Returns -0 if the provided argument is "-0"; returns a number @@ -33,7 +33,7 @@ export const canonicalNumericIndexString = ($) => { export const isArrayIndex = ($) => { const value = canonicalNumericIndexString($); if (value !== undefined) { - return Object.is(value, 0) || value > 0 && value < -1 >>> 0; + return sameValue(value, 0) || value > 0 && value < -1 >>> 0; } else { return false; } @@ -89,7 +89,7 @@ export const isConcatSpreadable = ($) => { export const isIntegerIndex = ($) => { const value = canonicalNumericIndexString($); if (value !== undefined) { - return Object.is(value, 0) || + return sameValue(value, 0) || value > 0 && value <= MAX_SAFE_INTEGER; } else { return false; diff --git a/function.js b/function.js new file mode 100644 index 0000000..0b9ebcc --- /dev/null +++ b/function.js @@ -0,0 +1,89 @@ +// ♓🌟 Piscēs ∷ function.js +// ==================================================================== +// +// Copyright © 2022 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 . + +import { defineOwnProperty, isObject } from "./object.js"; + +/** + * Creates a bound function from the provided function using the + * provided this value and arguments list. + * + * ☡ As with call and construct, the arguments must be passed as an + * array. + */ +export const bind = (() => { + const callBind = Function.prototype.call.bind( + Function.prototype.bind, + ); + const bind = ($, boundThis, boundArgs) => + callBind($, boundThis, ...boundArgs); + return bind; +})(); + +/** + * Calls the provided function with the provided this value and + * arguments list. + * + * ☡ This is an alias for Reflect.apply—the arguments must be passed + * as an array. + */ +export const call = Reflect.apply; + +/** + * Constructs the provided function with the provided arguments list + * and new target. + * + * ☡ This is an alias for Reflect.construct—the arguments must be + * passed as an array. + */ +export const construct = Reflect.construct; + +/** Returns whether the provided value is a constructor. */ +export const isConstructor = ($) => { + if (!isObject($)) { + // The provided value is not an object. + return false; + } else { + // The provided value is an object. + try { + construct( + function () {}, + [], + $, + ); // will throw if $ is not a constructor + return true; + } catch { + return false; + } + } +}; + +/** + * Returns a new function which calls the provided function with its + * first argument as the `this` value and the remaining arguments + * passed through. + * + * ※ This is effectively an alias for Function.prototype.call.bind. + */ +export const makeCallable = (() => { + const functionCall = Function.prototype.call; + const callable = ($) => defineOwnProperty( + bind(functionCall, $, []), + "length", + { value: $.length + 1 }, + ); + return callable; +})(); + +/** + * Returns whether the provided object inherits from the prototype of + * the provided function. + */ +export const ordinaryHasInstance = makeCallable( + Function.prototype[Symbol.hasInstance], +); diff --git a/iri.js b/iri.js index b9682d9..a3e4546 100644 --- a/iri.js +++ b/iri.js @@ -7,6 +7,13 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at . +import { bind } from "./function.js"; +import { + defineOwnProperties, + namedEntries, + objectFromEntries, +} from "./object.js"; + const sub·delims = String.raw`[!\$&'()*+,;=]`; const gen·delims = String.raw`[:/?#\[\]@]`; //deno-lint-ignore no-unused-vars @@ -167,8 +174,8 @@ export const { isLEIRIPath, isLEIRIReference, isLEIRISuffix, // only authority, path, query, fragment -} = Object.fromEntries( - Object.entries({ +} = objectFromEntries( + namedEntries({ isAbsoluteLEIRI: absolute·LEIRI, isAbsoluteIRI: absolute·IRI, isAbsoluteURI: absolute·URI, @@ -191,14 +198,14 @@ export const { const regExp = new RegExp(`^(?:${value})$`, "u"); return [ key, - Object.defineProperties( + defineOwnProperties( ($) => typeof $ == "string" && regExp.test($), { name: { value: key }, [Symbol.match]: { configurable: true, enumerable: false, - get: () => regExp[Symbol.match].bind(regExp), + get: () => bind(regExp[Symbol.match], regExp, []), set: undefined, }, }, @@ -266,8 +273,8 @@ export const escapeForIRI = ($) => { // Escape disallowed codepoints in each component and compose an // I·R·I from the result. return composeReference( - Object.fromEntries( - Object.entries(components).map( + objectFromEntries( + namedEntries(components).map( ([componentName, componentValue]) => [ componentName, componentValue == null ? undefined : [...function* () { diff --git a/mod.js b/mod.js index 9dd8fd1..d036cbc 100644 --- a/mod.js +++ b/mod.js @@ -9,6 +9,7 @@ export * from "./base64.js"; export * from "./collection.js"; +export * from "./function.js"; export * from "./iri.js"; export * from "./numeric.js"; export * from "./object.js"; diff --git a/object.js b/object.js index 8f5c490..c0a9081 100644 --- a/object.js +++ b/object.js @@ -7,6 +7,41 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at . +import { call } from "./function.js"; + +export const { + assign: assignProperties, + defineProperty: defineOwnProperty, + defineProperties: defineOwnProperties, + freeze, + getOwnPropertyDescriptor, + getOwnPropertyDescriptors, + getOwnPropertyNames: getOwnPropertyStrings, + getOwnPropertySymbols, + getPrototypeOf: getPrototype, + hasOwn: hasOwnProperty, + isExtensible, + isFrozen, + isSealed, + entries: namedEntries, + keys: namedKeys, + values: namedValues, + create: objectCreate, + fromEntries: objectFromEntries, + preventExtensions, + is: sameValue, + seal, + setPrototypeOf: setPrototype, +} = Object; + +export const { + delete: deleteOwnProperty, + keys: getOwnPropertyKeys, + get: getPropertyValue, + has: hasProperty, + set: setPropertyValue, +} = Reflect; + /** * A property descriptor object. * @@ -37,7 +72,7 @@ export const PropertyDescriptor = (() => { ); } else { // The provided value is an object. - const desc = Object.create(propertyDescriptorPrototype); + const desc = objectCreate(propertyDescriptorPrototype); if ("enumerable" in Obj) { // An enumerable property is specified. desc.enumerable = !!Obj.enumerable; @@ -228,8 +263,8 @@ export const PropertyDescriptor = (() => { const propertyDescriptorPrototype = PropertyDescriptor.prototype; - const propertyDescriptorProxyHandler = Object.assign( - Object.create(null), + const propertyDescriptorProxyHandler = assignProperties( + objectCreate(null), { defineProperty(O, P, Desc) { if ( @@ -264,11 +299,11 @@ export const PropertyDescriptor = (() => { ); } else { // P can be safely defined on O. - return Reflect.defineProperty(O, P, desc); + return defineOwnProperty(O, P, desc); } } else { // P is not a property descriptor attribute. - return Reflect.defineProperty(O, P, Desc); + return defineOwnProperty(O, P, Desc); } }, set(O, P, V, Receiver) { @@ -285,7 +320,7 @@ export const PropertyDescriptor = (() => { ); } else { // P can be safely defined on O. - return Reflect.set(O, prop, newValue, Receiver); + return setPropertyValue(O, prop, newValue, Receiver); } }, setPrototypeOf(O, V) { @@ -294,7 +329,7 @@ export const PropertyDescriptor = (() => { return false; } else { // V is the property descriptor prototype. - return Reflect.setPrototypeOf(O, V); + return setPrototype(O, V); } }, }, @@ -331,15 +366,15 @@ export const frozenCopy = (O, constructor = O?.constructor) => { // (If not provided, the constructor will be the value of getting // the `constructor` property of O.) const species = constructor?.[Symbol.species] ?? constructor; - return Object.preventExtensions( - Object.create( + return preventExtensions( + objectCreate( species == null || !("prototype" in species) ? null : species.prototype, - Object.fromEntries( + objectFromEntries( function* () { - for (const P of Reflect.ownKeys(O)) { - const Desc = Object.getOwnPropertyDescriptor(O, P); + for (const P of getOwnPropertyKeys(O)) { + const Desc = getOwnPropertyDescriptor(O, P); if (Desc.enumerable) { // P is an enumerable property. yield [ @@ -370,40 +405,12 @@ export const frozenCopy = (O, constructor = O?.constructor) => { } }; -/** Returns whether the provided value is a constructor. */ -export const isConstructor = ($) => { - if (!isObject($)) { - // The provided value is not an object. - return false; - } else { - // The provided value is an object. - try { - Reflect.construct( - function () {}, - [], - $, - ); // will throw if $ is not a constructor - return true; - } catch { - return false; - } - } -}; - /** Returns whether the provided value is an object. */ export const isObject = ($) => { return $ !== null && (typeof $ == "function" || typeof $ == "object"); }; -/** - * Returns whether the provided object inherits from the prototype of - * the provided function. - */ -export const ordinaryHasInstance = Function.prototype.call.bind( - Function.prototype[Symbol.hasInstance], -); - /** * Returns the primitive value of the provided object per its * `toString` and `valueOf` methods. @@ -423,7 +430,7 @@ export const ordinaryToPrimitive = (O, hint) => { const method = O[name]; if (typeof method == "function") { // Method is callable. - const result = method.call(O); + const result = call(method, O, []); if (!isObject(result)) { // Method returns a primitive. return result; @@ -470,7 +477,7 @@ export const toPrimitive = ($, preferredType) => { ); } else { // The resulting hint is either default, string, or number. - return exoticToPrim.call($, hint); + return call(exoticToPrim, $, [hint]); } } } else {