UNPKG

util-ex

Version:

Browser-friendly enhanced util fully compatible with standard node.js

111 lines (105 loc) 3.47 kB
import isFunctionStr from './is/string/function.mjs'; import isString from './is/type/string.mjs'; import { _createFunction as createFunc } from './_create-function.mjs'; /** * Creates an executable function with dynamic scope binding * * Unlike statically scoped functions, this rebinds scope variables on every execution, * allowing runtime updates to the execution environment. The function achieves this * through closure-delayed scope binding, regenerating the target function on each call. * * @example * // Basic usage * const scopedFunc = newScopedFunction( * 'add', * ['x'], * 'return x + y', * { y: 10 } // Initial scope * ); * scopedFunc(5); // Returns 15 * * // Dynamic scope update * const scope = { y: 20 }; * const updatableFunc = newScopedFunction('add', ['x'], 'return x + y', scope); * updatableFunc(5); // Returns 25 * scope.y = 100; * updatableFunc(5); // Returns 105 * * @param {string} name - Function name (for debugging and stack traces) * @param {string[]} argNames - Formal parameter names (array format) * @param {string} body - Function body code (as JavaScript string) * @param {Object} scope - Execution scope object (key-value pairs) * * @returns {Function} Executable function that: * - Accepts arguments defined in `argNames` * - Regenerates the function using current `scope` on each call * - Returns the execution result * * @see {@link newFunction} Underlying static-scope function generator * * @warning Performance note: Rebuilds function object on every call - * avoid in high-frequency scenarios * @note Scope dynamism: Always uses latest `scope` reference (pass-by-reference) * * @since 1.0.0 */ export function newScopedFunction(name, argNames, body, scope) { const params = normalizeFuncParams(name, argNames, body, scope) let funcName = params.name if (funcName === undefined) { const func = createFunc(params.body, params.scope); funcName = func.name; } params.createFunc = createFunc; const func = funcName ? createFunc(`function ${funcName}(...args) {return createFunc(body, scope)(...args);}`, params) : function(...args) { const func = createFunc(body, scope); return func(...args); } ; return func; } function normalizeFuncParams(name, argNames, body, scope) { if (!isFunctionStr(name)) { let async = ''; const vNameType = typeof name; if (vNameType === 'string') { if (name.startsWith('async ')) { name = name.substring(6); async = 'async ' } } else if (vNameType === 'object') { scope = name; name = ''; } else { name = ''; } if (isString(argNames)) { scope = argNames; body = argNames; argNames = []; } else if (!Array.isArray(argNames)) { argNames = []; } if (!body) {body = ''} body = `${async}function ${name}(${argNames.join(', ')}) {\n${body}\n}`; } else { body = name; name = undefined; } return {name, body, scope} } function getFuncName(name, argNames, body, scope) { const params = normalizeFuncParams(name, argNames, body, scope) if (params.name === undefined) { const func = createFunc(params.body, params.scope); params.name = func.name; } return params.name; } function newFunc(name, argNames, body, scope) { const params = normalizeFuncParams(name, argNames, body, scope) const func = createFunc(params.body, params.scope); return func }