通过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'变量赋予的参数如何/为什么作为'param'传递给doMysteriousThing函数。
如果有一个内部函数和一个外部函数,每个函数都接受一个参数,定义一个等于给定指定参数的外部函数的变量,这只是生活中的事实:
var mysteryFunction3 = makeMysterFunction(3);
将使得将参数发送到外部函数的变量实例,如:
mysteryFunction3(10)
会导致该参数(10)被读作内部函数的参数吗?
答案 0 :(得分: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!'
答案 1 :(得分: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
。不幸的是,它在命名法中丢失了
答案 2 :(得分: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#背景,这很疯狂。但是至少我现在明白了!