2

我正在尝试创建一个返回另一个函数的函数。当每个内部函数运行时,我想要单独的信息,但这没有发生。我知道解释不是很好,所以我整理了一个小例子。

            var testFn = function(testVal) {
                return (function(testVal) {
                    var test = testVal;

                    this.getVal = function() {
                        return test;
                    }

                    return that;

                })(testVal);
            }
            var a = testFn(4);
            var b = testFn(2);
            console.log(b.getVal(), a.getVal());

这输出 2、2。我想要输出 2、4。我知道这没有得到完美的解释,所以如果不清楚我想要实现什么,有人可以解释为什么这个变量似乎在两个函数之间共享吗?

谢谢

4

3 回答 3

3

像这样 ?

var testFn = function(testVal) {
  var test = testVal
  return {
    getVal: function() {
      return  test
    }
  }   
};

var ab = testFn (4)
var ac = testFn (2)

console.log(ab.getVal(),ac.getVal()) //4 //2

您的代码中的问题是this.getVal()/返回this

因为'this'指的是全局范围/Window

您正在使用全局命名空间并覆盖Window.getVal(),您设置的那一刻b = testFn (2) 这也会导致覆盖as 方法getVal,因为它们都引用全局 Object 并且总是共享相同的方法getVal

因此它们共享相同的闭包并输出 2

console.log("The same: " + (Window.a === Window.b)) // true
console.log("The same: " + (a === b)) // true

你可以看到,如果你稍微改变一下:

        var testFn = function(testVal) {
          var x = {}
            return (function(testVal) {
                var test = testVal;
                x.getVal = function () {
                  return test;
                }
                 return x

            })(testVal);
        }
        var a = testFn(4);
        var b = testFn(2);
        console.log(b.getVal(), a.getVal());//4 2

它突然起作用了,因为它导致返回 2 个不同的对象(顺便说一句,您甚至不需要外部封闭) console.log("The same: " + (a === b)) // false

这是JSbins First / Second

我希望您理解这一点,我不擅长解释事情如果有任何不清楚的地方,发表评论,我会尝试更新答案

于 2012-12-10T13:02:15.460 回答
2

这个问题归结为在 JavaScript 中调用函数的上下文。

在另一个函数中调用的函数在全局范围的上下文中执行。

在您的示例中,您有以下代码:

var testFn = function(testVal) {
  return (function(testVal) {
    var test = testVal;
    this.getVal = function() {
      return test;
    }
    return this;
  })(testVal);
}

内部函数在全局范围内被调用,因此this引用全局对象。在 JavaScript 中,在另一个函数中执行的函数是在其范围设置为全局范围的情况下完成的,而不是它所在函数的范围。这往往会使开发人员误会一点(或者至少,它对我有影响!)。

为了论证的缘故,假设 this 在浏览器中,因此this指的是window对象。这就是您被2记录两次的原因,因为第二次运行时,会this.getVal覆盖getVal您运行时定义的方法var a = testFn(4);

JavaScript 范围在函数级别,因此每个函数都有自己的范围:

var x = 3;
function foo() {
  var x = 2;
  console.log(x);
};
console.log(x); //gives us 3
foo(); // logs 2

因此,您要做的是在 function的上下文中运行该内部testFn函数,而不是在全局范围内。call您可以使用方法运行具有特定上下文的函数。我还录制了一个截屏视频callapply其中更详细地讨论了这一点。的基本用法call是:

function foo() {...}.call(this);

foothis. 因此,第一步是确保在正确的上下文(testFn方法的上下文)中调用您的内部函数。

var testFn = function(testVal) {
  return (function(testVal) {
    var test = testVal;
    this.getVal = function() {
      return test;
    }
    return this;
  }.call(this, testVal);
}

第一个参数call是上下文,后面的任何参数都作为参数传递给函数。所以现在内部函数在正确的范围内被调用,它不会添加getVal到全局范围内,这是朝着正确方向迈出的一步:)

接下来,尽管您还需要确保每次调用时都是在新范围内进行的,因此第二次调用时testFn不会覆盖。您可以使用关键字来执行此操作。这篇关于关键字的 SO 帖子非常值得一读。当您创建并执行 的新实例时,从而创建一个新范围。这个 SO question 也是相关的。this.getValtestFnnewnewvar foo = new testFn()testFN

您现在需要做的就是将您的声明更改ab

var a = new testFn(4);
var b = new testFn(2);

现在console.log(b.getVal(), a.getVal());将根据需要给予24

在 JSBin 上放了一个工作示例,它应该有助于解决问题。请注意此示例如何this.x在全局和函数内定义,并查看哪些被记录。玩这个,希望它可能有用。

于 2012-12-10T13:44:47.227 回答
1

你得到的输出是 (2,2) 因为当你这样做时

var that = this;

您实际得到的是全局对象(窗口),
该对象包含 javascript 代码中的所有全局方法和变量。
(请注意,未嵌套在对象或函数下的每个变量都是全局的,而未嵌套在对象
下的每个函数都是全局的,这意味着嵌套在函数下的函数仍然是全局的)

所以,当你设置:

var test = testVal;

this.getVal = function() {
    return test;
}

您实际上在全局对象中设置了函数“getVal”,并且在下一次运行中,您将再次设置相同的函数 - 覆盖第一个函数。
为了达到您想要的效果,我建议在内部函数中创建和对象并返回它(正如@Glutamat 在我之前建议的那样):

var testFn = function(testVal) {
        return new Object({
            getVal: function() {
                return testVal;
            }
        });
}
var a = testFn(4);
var b = testFn(2);
console.log(b.getVal(), a.getVal());

这样,在外部函数中,我们创建了一个带有名为“getVal”的内部函数的对象,该函数返回传递给外部函数(testVal)的变量。这是一个JSBin,如果你想玩的话
(感谢 @Glutamat 介绍这个网站,我从来没有听说过它,它真的很酷 :D)

于 2012-12-10T13:38:22.373 回答