Skip to content

Optimisation of reflection util functions #12

@aedart

Description

@aedart

Description

The following are a few optimization suggestions proposed by Google's AI (for when this project can be resumed).


Proposal: Performance Optimizations for Utility Functions

This proposal outlines a series of refactors for core utility functions to improve execution speed and reduce memory overhead in high-performance or high-frequency code paths.

Summary of Optimizations

  1. Allocation Reduction: We avoid "rest parameter" (...args) array allocations by leveraging the arguments object internally. We also move static helper objects (like DUMMY_ARGS) outside function bodies to prevent constant Garbage Collection (GC) churn.
  2. JIT-Friendly Paths: We implement "Fast Paths" to avoid expensive operations like try/catch and Reflect.construct in common scenarios (e.g., filtering out arrow functions early).
  3. Operation Efficiency: We utilize "loose inequality" (!= null) to check for both null and undefined in a single operation, which is highly optimized in modern JavaScript engines.
  4. Prototype Safety: We use Function.prototype.toString.call to ensure compatibility and safety against shadowed toString methods.

Optimized Utility Functions

/**
 * Cached references to avoid repeated lookups and allocations.
 */
const { construct: reflectConstruct } = Reflect;
const DUMMY_ARGS: any[] = [];
const DUMMY_CONSTRUCTOR = function() {};

/**
 * Determine if a function is a bound function.
 * Safe for non-function arguments and transpiled environments.
 * 
 * @param {unknown} argument The value to check.
 * @returns {boolean}
 */
export function isBoundFunction(argument: unknown): boolean {
    return (
        typeof argument === 'function' && 
        typeof argument.name === 'string' && 
        argument.name.startsWith('bound ')
    );
}

/**
 * Determine if given argument is a class constructor.
 * Optimized to avoid try/catch on the happy path and unnecessary string slicing.
 * 
 * @param {unknown} value The value to check.
 * @returns {boolean}
 */
export function isClassConstructor(value: unknown): boolean {
    if (typeof value !== 'function') {
        return false;
    }

    // Access string once via prototype to be safe from shadowed toString methods
    const fnStr = Function.prototype.toString.call(value);

    // O(1) check for 'class' keyword; fallback to regex only for leading whitespace
    return fnStr.startsWith('class ') || /^\s*class\b/.test(fnStr);
}

/**
 * Determine if given argument is a constructor.
 * Uses a "Fast Path" to skip expensive try/catch for arrow functions and
 * standard utilities, while remaining 100% spec-compliant via Reflect.
 * 
 * @param {unknown} argument The value to check.
 * @returns {boolean}
 */
export function isConstructor(argument: unknown): boolean {
    if (typeof argument !== 'function') {
        return false;
    }

    // Fast Path: Arrow fns lack prototypes and aren't bound. 
    // This avoids the expensive try/catch for most non-constructors.
    if (!(argument as Function).prototype && !isBoundFunction(argument)) {
        return false;
    }

    try {
        reflectConstruct(DUMMY_CONSTRUCTOR, DUMMY_ARGS, argument as Function);
        return true;
    } catch {
        return false;
    }
}

/**
 * Determine if value(s) are different from undefined and null.
 * Optimized via a named parameter fast-path and by avoiding Rest Parameter array allocation.
 * 
 * @param {any} [value] The first value to check.
 * @param {...any} [values] Additional values to check.
 * @returns {boolean}
 */
export function isset(value?: any, ...values: any[]): boolean {
    // Fast Path: Named parameter access is faster than arguments object lookup
    if (value == null) {
        return false;
    }

    const len = arguments.length;
    if (len === 1) {
        return true;
    }

    // Secondary Path: Iterate through arguments object to avoid array allocation
    for (let i = 1; i < len; i++) {
        if (arguments[i] == null) {
            return false;
        }
    }

    return true;
}

There are most likely many other utils that can be improved upon.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions