// ♓🌟 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 <https://mozilla.org/MPL/2.0/>.
-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],
);