js 动态调用函数

js动态调用函数

闭包 中,如果 onFinished​ 和 sayHello​ 是闭包内的局部函数,而不是全局函数或对象方法,那么直接通过函数名(字符串)调用会变得复杂,因为闭包内的函数无法通过 window​ 或全局对象访问。

以下是几种在闭包中实现动态调用函数的方法:


1. 使用函数映射(推荐)

在闭包中维护一个函数映射表(对象),将函数名与函数本身关联起来。

function createClosure() {
    // 闭包内的函数
    function sayHello(name) {
        console.log(`Hello, ${name}!`);
    }

    function add(a, b) {
        console.log(`Sum: ${a + b}`);
    }

    // 函数映射表
    const functionMap = {
        sayHello,
        add
    };

    // onFinished 实现
    function onFinished(functionName, ...args) {
        if (typeof functionMap[functionName] === 'function') {
            functionMap[functionName](...args); // 通过映射表调用函数
        } else {
            console.error(`函数 ${functionName} 不存在或不是一个函数`);
        }
    }

    return onFinished;
}

// 创建闭包并获取 onFinished
const onFinished = createClosure();

// 调用 onFinished
onFinished('sayHello', 'Alice'); // 输出: Hello, Alice!
onFinished('add', 2, 3); // 输出: Sum: 5
onFinished('nonExistentFunction'); // 输出: 函数 nonExistentFunction 不存在或不是一个函数

2. 使用 eval(不推荐)

在闭包中,可以通过 eval​ 动态调用局部函数,但这种方法存在安全性和性能问题,不推荐使用

function createClosure() {
    // 闭包内的函数
    function sayHello(name) {
        console.log(`Hello, ${name}!`);
    }

    function add(a, b) {
        console.log(`Sum: ${a + b}`);
    }

    // onFinished 实现
    function onFinished(functionName, ...args) {
        try {
            eval(`${functionName}(${args.map(arg => JSON.stringify(arg)).join(',')})`);
        } catch (error) {
            console.error(`函数 ${functionName} 不存在或执行出错:`, error);
        }
    }

    return onFinished;
}

// 创建闭包并获取 onFinished
const onFinished = createClosure();

// 调用 onFinished
onFinished('sayHello', 'Bob'); // 输出: Hello, Bob!
onFinished('add', 5, 7); // 输出: Sum: 12
onFinished('nonExistentFunction'); // 输出: 函数 nonExistentFunction 不存在或执行出错: ReferenceError

3. 将函数暴露到闭包外

如果闭包内的函数需要被外部调用,可以将它们暴露到闭包外(例如,返回一个包含函数的对象)。

function createClosure() {
    // 闭包内的函数
    function sayHello(name) {
        console.log(`Hello, ${name}!`);
    }

    function add(a, b) {
        console.log(`Sum: ${a + b}`);
    }

    // onFinished 实现
    function onFinished(functionName, ...args) {
        if (typeof functions[functionName] === 'function') {
            functions[functionName](...args); // 调用暴露的函数
        } else {
            console.error(`函数 ${functionName} 不存在或不是一个函数`);
        }
    }

    // 暴露函数
    const functions = { sayHello, add };

    return { onFinished, functions };
}

// 创建闭包并获取 onFinished 和 functions
const { onFinished, functions } = createClosure();

// 调用 onFinished
onFinished('sayHello', 'Charlie'); // 输出: Hello, Charlie!
onFinished('add', 10, 20); // 输出: Sum: 30

// 也可以直接调用暴露的函数
functions.sayHello('Dave'); // 输出: Hello, Dave!

4. 使用 arguments.callee(已弃用)

在旧版 JavaScript 中,可以使用 arguments.callee​ 获取当前函数,然后通过函数名调用闭包内的函数。但这种方法 已被弃用,不推荐使用。

function createClosure() {
    // 闭包内的函数
    function sayHello(name) {
        console.log(`Hello, ${name}!`);
    }

    function add(a, b) {
        console.log(`Sum: ${a + b}`);
    }

    // onFinished 实现
    function onFinished(functionName, ...args) {
        const caller = arguments.callee.caller; // 获取调用者
        if (typeof caller[functionName] === 'function') {
            caller[functionName](...args); // 调用闭包内的函数
        } else {
            console.error(`函数 ${functionName} 不存在或不是一个函数`);
        }
    }

    return onFinished;
}

// 创建闭包并获取 onFinished
const onFinished = createClosure();

// 调用 onFinished
onFinished('sayHello', 'Eve'); // 输出: Hello, Eve!
onFinished('add', 3, 4); // 输出: Sum: 7

5. 总结

在闭包中动态调用函数时,推荐使用 函数映射表 的方式(方法 1),因为它既安全又灵活。如果闭包内的函数需要被外部调用,可以将它们暴露到闭包外(方法 3)。

避免使用 eval​ 和 arguments.callee​,因为它们会带来安全性和维护性问题。

image.png

留下你的脚步
推荐阅读