跟踪高阶函数中的变量

时间:2019-04-01 21:11:41

标签: javascript higher-order-functions

任何人都可以解释一下运行该函数后变量值如何存在吗? 我看到了高阶函数的代码示例,但我不明白在每次运行函数后如何跟踪函数中的变量。 在下面的示例中,每次运行后变量计数如何添加更多?

// Higher order functions

// A higher order function is any function that does at least one of the following
//   1. Accepts a function as an argument
//   2. Returns a new function

// Receives a function as an argument
const withCount = fn => {
  let count = 0

  // Returns a new function
  return (...args) => {
    console.log(`Call counts: ${++count}`)
    return fn(...args)
  }
}

const add = (x, y) => x + y

const countedAdd = withCount(add)

console.log(countedAdd(1, 2))
console.log(countedAdd(2, 2))
console.log(countedAdd(3, 2))

3 个答案:

答案 0 :(得分:1)

请注意,withCount函数仅被调用一次:

const countedAdd = withCount(add)

在该调用之后,将创建变量count,并且由于它们仍在与该变量相同的作用域中具有可能的引用,因此不会破坏该变量,从而可以在该作用域内使用。

请注意,返回的箭头函数在范围内(withCount函数)。

答案 1 :(得分:1)

如果¹该怎么办? 如果只有一棵树,可以说是物体怎么办?每个对象都有一个键-值对列表以及对其父对象的引用。现在我们如何用那棵树模拟变量?对于易于使用的全局变量,我们必须以某种方式引用“全局范围对象”,然后可以在其中添加键值对:

 // var test = "value";
 global.test = "value";

现在我们如何表示本地范围?非常简单:只要调用一个函数,我们就创建一个新的此类对象,并让父引用指向根对象。

 local.parent -> global
 local.count = 0;

现在,从该局部函数作用域中,我们可以查找局部变量(例如count)和全局变量,只需遍历当前作用域的父级并检查那里的变量(test例如)。

对于函数内部的函数呢?也很容易:我们只是让当前作用域对象的父对象指向外部函数之一:

 local2.parent -> local

现在可以在该内部范围中查找count,我们可以转到parent并在那里找到它作为属性。

现在的诀窍是,当函数结束执行时,这些“上下文对象”不会消失,而是在所有对它的引用都丢失时消失。

现在,我们需要另一个技巧来使您的示例生效: 函数声明必须保留对其父作用域的引用,因此,在调用该函数时,我们可以让局部作用域parent指向父作用域。

因此,如果执行return (...args) => {,则将保留对“当前作用域对象”(包含count)的引用,并将其存储在函数中。当您调用它时,该函数将使用新的作用域对象执行,并且可以通过父引用访问count。只要保留对该函数的引用,该“作用域对象”的内部引用就会被保留,并且包含count


¹实际上,这正是发生的情况。 ECMA规范称这些“范围对象” EnvironmentRecord ...

答案 2 :(得分:0)

您在这里看到的东西称为闭包。您可以找到许多很好的文章,书籍来解释这一概念。基本上,它是对变量的隐式引用。

dput(qq)
structure(list(my_year = c(2004, 2004, 2004, 2004, 2004, 2004, 
2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 
2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 
2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 
2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 
2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 
2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 
2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 
2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 
2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004), my_time = structure(c(72484L, 
37360L, 57326L, 57240L, 26966L, 48196L, 48047L, 47931L, 71027L, 
78931L, 32144L, 40831L, 31545L, 31092L, 73992L, 39895L, 76988L, 
70303L, 52993L, 77522L, 53289L, 43273L, 52609L, 58788L, 69625L, 
83071L, 60847L, 62218L, 75594L, 58615L, 38332L, 45811L, 75290L, 
3063L, 67321L, 74520L, 74248L, 47665L, 54416L, 33803L, 32515L, 
32428L, 40518L, 61085L, 63825L, 66352L, 73773L, 67165L, 37659L, 
47710L, 49206L, 72484L, 37360L, 57326L, 57240L, 26966L, 48196L, 
48047L, 47931L, 71027L, 78931L, 32144L, 40831L, 31545L, 31092L, 
73992L, 39895L, 76988L, 70303L, 52993L, 77522L, 53289L, 43273L, 
52609L, 58788L, 69625L, 83071L, 60847L, 62218L, 75594L, 58615L, 
38332L, 45811L, 75290L, 3063L, 67321L, 74520L, 74248L, 47665L, 
54416L, 33803L, 32515L, 32428L, 40518L, 61085L, 63825L, 66352L, 
73773L, 67165L, 37659L, 47710L, 49206L), class = "ITime")), class = "data.frame", row.names = c(NA, 
-102L), .Names = c("my_year", "my_time"))

因此,当您呼叫The returned function below closes over variable `count` return (...args) => ... 时,将保留对withCount的秘密引用。还有您调用的其他函数,只需保持与该变量的交互即可。