这对JavaScript函数有何不同?

3

我希望你能帮助我理解JavaScript闭包的工作方式。请看以下两个函数,并告诉我它们之间的不同之处,因为它们在多次调用时产生完全不同的结果:

函数1

var add = (function() {
  var counter = 0;
  return function() {
    return counter += 1;
  }
})();
console.log(add()); // result is 1
console.log(add()); // result is 2
console.log(add()); // result is 3

Function 2

function add() {
  var counter = 0;
  function() {
    return counter += 1;
  }
  plus();
}
console.log(add()); // result is 1
console.log(add()); // result is 1
console.log(add()); // result is 1


1
在第二个例子中,每次调用add()时,counter的值都会被设置为0 - Rayon
5
第二个例子实际上是无效的。我猜你的意思是 function plus() {} - Felix Kling
1
@FelixKling,然后也要return plus();... - Rayon
2
@Aditya - 让 OP 在代码中进行这些更改。这个编辑将改变代码的含义。 - Rayon
这里有一个关于 JavaScript 函数闭包的解释:http://www.w3schools.com/js/js_function_closures.asp - rejo
6个回答

3
在第一个例子中,声明了变量counter,并在调用add时调用的函数本质上是:
function (){
    return counter += 1;
}

这很重要,因为每次调用add函数时,counter并未被重新声明。

在第二个例子中,每次调用add函数时,counter都会被重新声明。


2
在情况1中,您返回一个在外部IIFE上具有闭包的函数。因此,即使在返回后,返回的函数也能够存储count的值(即Closure),并且每次调用add()时都会得到更新的值。
您的情况2似乎不正确。您没有定义plus函数。即使您定义了它,由于您没有从add()返回任何内容,您将获得undefined。

function add() {
  var counter = 0;
  function plus () {
    return counter += 1;
  }
  plus();
}
console.log(add()); // result is 1
console.log(add()); // result is 1
console.log(add()); // result is 1

当你从中返回plus(),你只会得到1,因为实际上你每次都返回执行counter += 1的结果,而每次在调用add()counter被重置为0

确实,那是一个疏忽。 - pedroyanky
所以你的意思是说,在函数一中,计数器并没有在每次调用时被重新声明?这怎么可能?因为我在某个地方学到,每当函数被调用时,都会创建一个新的执行上下文。那么这两者之间的区别真正在哪里呢? - pedroyanky
1
@pedroyanky:包含“var counter = 0;”的函数只执行一次。内部函数(return function() {...})是你每次调用add()时所谓的“每次”的内容。两个示例之间的区别在于,第一个示例执行了var add = (function() {...}()),而第二个示例执行了var add = function(){};。第一个示例执行外部函数一次,而第二个示例执行外部函数“每次”。因为“我在某个地方学到,每次调用函数时,都会创建一个新的执行上下文”,这就是为什么第二种情况中counter被重置的原因。 - Felix Kling

2
这对JavaScript函数有何不同?
每个示例中,您都在声明两个函数,其中一个嵌套在另一个内部。
在第一个示例中,您调用外部函数一次,返回内部函数并将其分配给add。这意味着调用add将仅执行内部函数。
在第二个示例中,add指的是外部函数。因此,调用add将始终执行外部和内部函数。
也许如果我们稍微改写第一个示例(它仍会产生相同的结果),您会更清楚地看到区别。
// Example 1
function add() {
  var counter = 0;
  return function plus() {
    return counter += 1;
  }
}

var plus = add();
console.log(plus());
console.log(plus());

注意到add只被调用一次,这意味着var counter = 0;只会被执行一次,而我们实际上调用的是plus函数。因为plus是在add内定义的,所以它可以访问counter变量。

1

在封闭区域之外,您无法访问任何在封闭区域内定义的变量。这有助于避免冲突,例如如果多个函数使用相同的变量名称。


0
在第一个例子中,外部函数只运行一次,它将计数器变量设置为零。外部函数返回内部函数,并将其分配给变量add。当您调用add()时,内部函数在声明时的上下文中运行,因此它可以增加计数器变量。
在第二个例子中,声明了名为add的外部函数。每次调用add时都会运行这个外部函数。这个外部函数每次都将计数器设置为零,然后运行内部的plus函数,该函数将计数器增加到1并返回它。

这很有帮助,谢谢。我现在的理解是计数器的值被存储在变量add中。由于匿名函数被存储在一个变量中,可以安全地说,每当函数被调用时,变量add会接收到计数器的更新版本吗? - pedroyanky
计数器并没有存储在变量中,而是存储在函数中。每当函数运行时,它仍然可以访问在声明时拥有的所有内容,包括counter变量,即使外部函数不再运行,该变量仍然存在于内存中。 - dtkaias
是的,我觉得我现在真的掌握了它。谢谢。 - pedroyanky

0
这是第一段代码片段中确切发生的事情。
var add = (function() {
  var counter = 0;
  return function() {
    return counter += 1;
  }
})();

在上述语句中,您将变量add分配给一个增加counter的函数。

需要注意的事项:

  1. 通过执行封闭函数来获取此函数。(请注意封闭函数返回的内容)

  2. 通过在封闭函数后面放置()来执行封闭函数。

现在,变量add是一个仅增加counter的函数。这里设置了环境以创建闭包-因此内部函数始终记住counter的先前值。

因此,每次通过add变量执行内部函数时,您都会看到增加的数字。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接