2

通过 Javascript Koans 工作,我开始关注以下代码:

    it("should use lexical scoping to synthesise functions", function () {

function makeMysteryFunction(makerValue)
{
  var newFunction = function doMysteriousThing(param)
  {
    return makerValue + param;
  };
  return newFunction;
}

var mysteryFunction3 = makeMysteryFunction(3);
var mysteryFunction5 = makeMysteryFunction(5);

expect(mysteryFunction3(10) + mysteryFunction5(5)).toBe(FILL_ME_IN);
});

它得到23,这很好。我感到困惑的是,赋予“mysteryFunction3”变量的参数如何/为什么作为“参数”传递给 doMysteriousThing 函数。

如果有一个内部函数和一个外部函数,每个函数都采用一个参数,那么在给定指定参数的情况下定义一个等于外部函数的变量,这只是生活中的事实,例如:

    var mysteryFunction3 = makeMysterFunction(3);

将使其向外部函数的变量实例发送参数,例如:

   mysteryFunction3(10)

会导致该参数(10)被读取为内部函数的参数吗?

4

3 回答 3

1

一开始我也很难理解这一点。这就是我如何通过一击一击达到清晰。这是我在堆栈上的第一篇文章,请原谅我对此啰嗦。

让我们看一个返回字符串的非常基本的函数:

function fun() {
       return 'Hi!';
    }

如果我们在没有调用括号的情况下记录上述函数,控制台只会记录对该函数的引用:

console.log(fun); //logs ---> [Function: fun] 

如果我们再次记录它,但带有调用括号:

console.log(fun()); //logs ---> Hi!

函数的调用等于它的返回值:

console.log(fun() === 'Hi!'); //logs ---> true

因此,在此基础上,让我们重写我们的函数,在其中声明另一个返回字符串的函数。外部函数将返回内部函数的调用:

function funAgain() {
   function innerFun() {
     return 'Hello there!';
   }
   return innerFun();
}

因此,在 funAgain (innerFun() === 'Hello there!') 的范围内,计算结果为 true,因此当我们将 funAgain 调用记录到控制台时:

console.log(funAgain()); //logs ---> 'Hello there!'

但是,如果我们在外部函数的 return 语句中去掉调用 innerFun 的括号会怎样?

function funAgain() {
  function innerFun() {
    return 'Hello there!';
  }
  return innerFun;
}

console.log(funAgain()); //logs [Function: innerFun]

返回函数 ITSELF。虽然这实际上不是全部,但我们可以想到 (funAgain() === innerFun) 显然,由于范围问题,您实际上无法在实践中运行此比较(innerFun 不能存在于 funAgain 的调用之外) . 但!让我们这样想一下。这意味着如果我们在一个变量中捕获 funAgain 的返回值:

var innerFunCaptured = funAgain();

console.log(innerFunCaptured); // logs [Function: innerFun]

从概念上讲,我们有 (innerFunCaptured === innerFun) ...

因此,现在我们的变量已绑定到内部函数,我们可以通过向变量添加括号来调用该内部函数。

console.log(innerFunCaptured()); //logs ---> 'Hello there!'

当我在谈论上面的“整个故事”时,我遗漏的是内部函数与变量的绑定是外部函数调用的结果,所以实际上绑定不仅包括 innerFun 本身,还包括它的环境是在包含通过调用外部函数传递的任何潜在参数中创建的,这使我们能够...

再次重写外部函数和内部函数,使它们现在具有交互的参数:

function funOnceMore(greetingPartOne) {
  function innerFun(greetingPartTwo) {
    return greetingPartOne + ' ' + greetingPartTwo;
  }
  return innerFun;
}

如果我们用参数记录 funOnceMore 会怎样。

console.log(funOnceMore('Hello')) //logs ---> [Function: innerFun]

同样,innerFun 本身被返回。但是我们传递的参数 greetingPartOne 呢?好吧,它通过了,但是由于在 funOnceMore 中从未调用过 innerFun,因此从未以任何有意义的方式使用 greetingPartOne。我们必须弄清楚如何调用 innerFun!答案:我们需要将它绑定到一个变量,就像我们在上一步中所做的那样。

var innerFunCapturedAgain = funOnceMore('Hello')

现在 innerFunCapturedAgain 拥有 innerFun 和 funOnceMore 的环境以及我们传递给它的参数“Hello”。

所以现在我们可以通过在innerFunCapturedAgain 上加上括号来调用innerFun,这些括号将封装我们传递给innerFun 的greetingPartTwo 的参数。

console.log(innerFunCapturedAgain('there!')) //logs ---> 'Hello there!'
于 2021-03-15T04:11:08.440 回答
0

这两个答案都非常有帮助,但是我自己也在努力解决这个问题,我认为最好的答案是了解正在发生的事情并对其进行更改以对其有新的认识:

makeMysteryFunction创建一个函数,将其参数 ( makerValue) 添加到传递给它返回的函数 ( mysteryFunctionX) 的参数。

所以,帮助我理解的是将数字转换为字符串:

function makeGreeting(greeting)
{
  var newFunction = function greet(name)
  {
    return greeting + ' ' + name;
  };
  return newFunction;
}

var makeGreetingHi = makeGreeting('Hi');
var makeGreetingHello = makeGreeting('Hello');
//finally call functions
makeGreetingHi('Stranger');
//Hi Stranger
makeGreetingHello('Friend');
//Hello Friend

我所做的只是更改函数的名称,并连接字符串而不是添加数字。让公案混淆的是函数名称:猜猜这是不良做法的一个很好的例子。练习的重点很简单,在我提供的示例中,该greet函数可以访问greeting. 不幸的是,这在命名法中丢失了

于 2014-05-13T03:19:28.420 回答
0

我刚刚发现对理解这里到底发生了什么非常有用的东西是添加一个 console.log 来显示“mysteryFunction3”的内容。所以:

var mysteryFunction3 = makeMysteryFunction(3);
var mysteryFunction5 = makeMysteryFunction(5);

console.log(mysteryFunction3);

expect(mysteryFunction3(10) + mysteryFunction5(5)).toBe(FILL_ME_IN);

控制台输出是这样的:

doMysteriousThing(param)
  {
    return makerValue + param;
  }

来自 C# 背景,这太疯狂了。但至少我现在明白了!

于 2018-06-21T21:01:31.593 回答