Nodejs 匿名函数引用外部变量是引用类型的问题

Nodejs 匿名函数引用外部变量是引用类型的问题

var obj = {};
var funNames = [‘get’, ‘set’];
for(var item in funNames){
var funName = funNames[item];
obj[funName] = function(){
console.log(funName);
}
}
obj.get();
obj.set();

最后两个都输出 set, 我想实现的是一个输出 get, 一个输出set


8 回复

Node.js 匿名函数引用外部变量是引用类型的问题

在 JavaScript 中,尤其是使用 Node.js 时,匿名函数常常会引用外部的变量。然而,这可能会导致一些意外的行为,特别是在循环中定义函数时。让我们通过一个具体的例子来说明这个问题,并提供解决方案。

示例代码

var obj = {};
var funNames = ['get', 'set'];

for (var item in funNames) {
    var funName = funNames[item];
    obj[funName] = function() {
        console.log(funName);
    };
}

obj.get(); // 输出 "set"
obj.set(); // 输出 "set"

在这个例子中,我们期望 obj.get() 输出 "get"obj.set() 输出 "set"。但实际上,两者都输出 "set"。这是因为循环中的 funName 变量在每次迭代中都被更新,而函数引用的是同一个 funName 变量。因此,在循环结束后,所有函数都引用了最后一次赋值后的 funName 值。

解决方案

为了确保每个函数都能正确地引用到它们在循环中对应的值,可以使用闭包或者立即执行函数表达式(IIFE)来捕获当前的 funName 值。

使用闭包
var obj = {};
var funNames = ['get', 'set'];

for (var item in funNames) {
    var funName = funNames[item];
    (function(fn) { // 创建一个新的作用域
        obj[fn] = function() {
            console.log(fn);
        };
    })(funName); // 立即执行函数表达式
}

obj.get(); // 输出 "get"
obj.set(); // 输出 "set"
使用 let 声明

另一种更简洁的方法是使用 let 关键字声明变量,这样可以在每次迭代中创建一个新的块级作用域,从而避免引用问题。

var obj = {};
var funNames = ['get', 'set'];

for (let item in funNames) { // 使用 let 替换 var
    let funName = funNames[item];
    obj[funName] = function() {
        console.log(funName);
    };
}

obj.get(); // 输出 "get"
obj.set(); // 输出 "set"

通过以上两种方法,我们可以确保每个函数都能正确地引用到它们在循环中对应的 funName 值。


用闭包可以实现

var obj = {};
var funNames = ['get', 'set'];
for(var item in funNames){
  var funName = funNames[item];
  function some(funName){
    return function() {
      console.log(funName);
    }
  }
  obj[funName] = some(funName);
}
obj.get();
obj.set();
var obj = {};
var funNames = ['get', 'set'];
for(var item in funNames){
    var funName = funNames[item];
    //动态生成function
    obj[funName] = new Function("console.log('" + funName + "');");
}
obj.get();
obj.set();

可以实现我的功能 非常感谢.

这种方式也可以解决我的问题, 谢谢
不过有一点点不易于扩展…

var obj = {};
var funNames = ['get', 'set'];
funNames.forEach(function(funName) {
    obj[funName] = function(){
        console.log(funName);
    }
});
obj.get();
obj.set();

这个最简洁最有效

在这个问题中,问题的核心在于匿名函数对外部变量的引用方式。由于funName是在循环中定义的,当循环结束后,funName的值会变成循环的最后一个值,即'set'。因此,在调用obj.get()obj.set()时,它们都会输出set

这是因为JavaScript中的闭包捕获的是变量的引用,而不是变量在当前作用域内的值。所以在创建匿名函数时,它实际上引用的是同一个funName变量。当循环结束时,funName的值为'set',所以所有匿名函数都打印出'set'

解决方案

可以通过立即执行函数表达式(IIFE)来捕获每个迭代中的funName变量的当前值:

var obj = {};
var funNames = ['get', 'set'];

for (var item in funNames) {
    (function(funName) { // IIFE
        obj[funName] = function() {
            console.log(funName);
        }
    })(funNames[item]);
}

obj.get(); // 输出 "get"
obj.set(); // 输出 "set"

解释

通过使用IIFE,我们为每次迭代创建了一个新的作用域,并将funName作为参数传递给该作用域。这样,每个匿名函数都有自己的funName副本,而不是共享同一个变量。因此,当调用obj.get()obj.set()时,它们会分别输出getset

这种方法确保了每个匿名函数能够访问到循环中正确迭代的变量值,从而避免了引用类型导致的问题。

回到顶部