2

Working through Javascript Koans, I'm getting hung up on the following code:

    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);
});

It gets 23, and that's fine. The thing I'm puzzled by is how/why the parameter given to the 'mysteryFunction3' variable gets passed to the doMysteriousThing function as 'param'.

Is it just a fact of life that if there's an inner and an outer function which each take one parameter, defining a variable equal to the outer function given a specified parameter like:

    var mysteryFunction3 = makeMysterFunction(3);

will make it so that sending a parameter to the variable instance of the outer function, like:

   mysteryFunction3(10)

will cause that parameter (10) to be read as the parameter for the inner function?

2
  • JavaScript functions form a closure over the variable scope where they were originally defined. The doMysteriousThing function was created in the variable scope where the makerValue was passed. This gives that function the ability to reference makerValue even though the function is returned and assigned to your mysteryFunction3. As you can see, it uses it in its return value return makerValue + param; Where makerValue again was passed to makeMysteryFunction(), and param is passed to doMysteriousThing() Commented Jul 12, 2013 at 0:07
  • 2
    It may be less confusing if you didn't have 3 different names for the same function. The newFunction, doMysteriousThing and mysteryFunction3 all refer to the same function. To clean it up, I'd get rid of the newFunction and doMysteriousThing names, and just do return function(param) { return makerValue + param; }; Commented Jul 12, 2013 at 0:11

3 Answers 3

1

I struggled through understanding this as well at first. This is how I reached clarity with it blow for blow. This is my first post on stack and excuse my being long-winded about it.

Let's take a very basic function that returns a string:

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

If we log the above function without the invocation parentheses, the console just logs a reference to the function:

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

If we log it again, but WITH the invocation parenthesis:

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

the invocation of a function is equal to its return value:

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

So building off of that, let's rewrite our function to have another function declared within it that returns a string. The outer function will return the invocation of the inner function:

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

So within the scope of funAgain (innerFun() === 'Hello there!') evaluates to true so when we log an invocation of funAgain to the console:

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

But what if we left off the invoking parentheses of innerFun in the return statement of the outer function?

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

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

The function ITSELF is returned. Though it's not actually the whole story, we could think of (funAgain() === innerFun) Obviously, you can't actually run this comparison in practice because of scoping issues (innerFun can't exist outside of an invocation of funAgain). BUT! Let's for a moment think of it that way. That means that if we capture the return value of funAgain in a variable:

var innerFunCaptured = funAgain();

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

We have, again conceptually, (innerFunCaptured === innerFun) ...

So now that our variable is bound to the inner function, we can invoke that inner function by adding parentheses to the variable.

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

When I was talking about the "whole story" above, what I left out was that the inner function's binding to the variable is the result of the outer function's invocation, so really the binding not only includes innerFun itself, but ALSO the environment which it was created within including any potential arguments passed through the invocation of the outer function, which allows us to...

Rewrite the outer and inner function once more so that they now have parameters which interact:

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

What if we log funOnceMore with an argument.

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

Again, innerFun itself is returned. But what about the argument greetingPartOne we passed? Well, it was passed alright, but since innerFun was never invoked within funOnceMore, greetingPartOne was never used in any meaningful way. We have to figure out how to invoke innerFun! The answer: we need to bind it to a variable like we did in the previous step.

var innerFunCapturedAgain = funOnceMore('Hello')

Now innerFunCapturedAgain holds innerFun AND the environment of funOnceMore with the argument 'Hello' that we passed into it.

So now we can invoke innerFun by putting parentheses on innerFunCapturedAgain, and those parentheses will encapsulate the argument for greetingPartTwo that we pass to innerFun.

console.log(innerFunCapturedAgain('there!')) //logs ---> 'Hello there!'
Sign up to request clarification or add additional context in comments.

Comments

0

Both the answers are very helpful, but struggling through this myself, I think the best answer is to just walk through what's happening and change it just enough to shed a new light on it:

makeMysteryFunction makes a function which adds its argument (makerValue) to the argument that's being passed to the function that it returns (mysteryFunctionX).

So, what helped me understand this is converting the numbers to strings:

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

All I did was change the name of the functions, and concatenate strings instead of adding numbers. What makes the koan confusing is the function names: guess thats a good example of bad practices. The point of the exercise is simply that in the example I provided the greet function has access to greeting. Unfortunately thats lost in the nomenclature

Comments

0

Something that I just found extemely usefull in understanding what exactly is going on here is adding a console.log to show the contents of "mysteryFunction3". So:

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

console.log(mysteryFunction3);

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

The console output was this:

doMysteriousThing(param)
  {
    return makerValue + param;
  }

Coming from a C# background, this is crazy. But at least I understand it now!

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.