X-Git-Url: https://git.ladys.computer/Pisces/blobdiff_plain/6f1ff895670d04034ef09faea4779923a85097fb..83f6aae0d1b8181dc2b0c6ccdba9f2fe2fdba3e6:/function.js?ds=sidebyside diff --git a/function.js b/function.js index 0b9ebcc..5f397a9 100644 --- a/function.js +++ b/function.js @@ -1,89 +1,138 @@ // ♓🌟 Piscēs ∷ function.js // ==================================================================== // -// Copyright © 2022 Lady [@ Lady’s Computer]. +// 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 . -import { defineOwnProperty, isObject } from "./object.js"; +import { ITERATOR } from "./value.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; +export const { + /** + * 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. + */ + bind, + + /** + * 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::call.bind`. + */ + makeCallable, +} = (() => { + // ☡ Because these functions are used to initialize module constants, + // they can’t depend on imports from elsewhere. + const { + bind: functionBind, + call: functionCall, + } = Function.prototype; + const callBind = Reflect.apply(functionBind, functionCall, [ + functionBind, + ]); + const { + create: objectCreate, + defineProperty: defineOwnProperty, + getPrototypeOf: getPrototype, + } = Object; + const { [ITERATOR]: arrayIterator } = Array.prototype; + const { + next: arrayIteratorNext, + } = getPrototype([][ITERATOR]()); + const argumentIterablePrototype = { + [ITERATOR]() { + return { + next: callBind( + arrayIteratorNext, + call(arrayIterator, this.args, []), + ), + }; + }, + }; + return { + bind: ($, boundThis, boundArgs) => + callBind( + $, + boundThis, + ...objectCreate( + argumentIterablePrototype, + { args: { value: boundArgs } }, + ), + ), + makeCallable: ($) => + defineOwnProperty( + callBind(functionCall, $), + "length", + { value: $.length + 1 }, + ), + }; })(); -/** - * 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; +export const { + /** + * 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 arraylike. + */ + apply: call, + + /** + * 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 arraylike. + */ + construct, +} = Reflect; /** - * Constructs the provided function with the provided arguments list - * and new target. + * Returns the provided value. * - * ☡ This is an alias for Reflect.construct—the arguments must be - * passed as an array. + * ※ This function can be called as a constructor. When used in an + * `extends` clause and called via `super`, it will set the value of + * `this` to the provided value, enabling it to be extended with + * private class features. */ -export const construct = Reflect.construct; +export const identity = function ($) { + return $; +}; + +/** Returns whether the provided value is callable. */ +export const isCallable = ($) => typeof $ === "function"; /** Returns whether the provided value is a constructor. */ export const isConstructor = ($) => { - if (!isObject($)) { - // The provided value is not an object. + // The provided value is an object. + try { + // Try constructing a new object with the provided value as its + // `new.target`. This will throw if the provided value is not a + // constructor. + construct( + function () {}, + [], + $, + ); + return true; + } catch { + // The provided value was not a constructor. 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], + Function.prototype[Symbol.hasInstance], );