Ad

Alternative To Closure

- 1 answer

I am uncertain how to extract some code into a function that needs access to a variable. Most languages have quite clear conventions, but in Javascript everything seems possible.

Here I am doing some calculations with a variable stack that I would like to extract to a function.

function process() {
  var stack = [];

  while (work_to_do) {
    var chunk = get_next_chunk();

    stack.push(do_some_calculations(stack, chunk));         //
    stack.push(do_more_fancy_calculations(stack, chunk));   // to extract
    stack.push([ stack.pop(), stuff, stack.pop() ]);        //
  }

  stack.push(do_some_calculations(stack, special_chunk));
  stack.push(do_more_fancy_calculations(stack, special_chunk));
  stack.push([ stack.pop(), 'stuff', stack.pop() ]);
}

Now I think the usual way (in Javascript) would be to define the function inside of function process() to give it access to the closure variable stack:

function process() {
  var stack = [];

  var do_calculations = function (chunk) {
    stack.push(do_some_calculations(stack, chunk));
    stack.push(do_more_fancy_calculations(stack, chunk));
    stack.push([ stack.pop(), 'stuff', stack.pop() ]);
  }

  while (work_to_do) {
    do_calculations(get_next_chunk());
  }

  do_calculations(special_chunk);
}

However, (the real) function process() is already quite cluttered and deeply indented, so it might be better to have a pure function on the top level to do those calculations. I could pass stack as a parameter:

function process() {
  var stack = [];

  while (work_to_do) {
    do_calculations(stack, get_next_chunk());
  }

  do_calculations(stack, special_chunk);
}

function do_calculations (stack, chunk) {
  stack.push(do_some_calculations(stack, chunk));
  stack.push(do_more_fancy_calculations(stack, chunk));
  stack.push([ stack.pop(), 'stuff', stack.pop() ]);
}

Or I could treat stack like some kind of class object:

stack = {
  data: [],
  do_calculations: function (chunk) {
    this.data.push(do_some_calculations(stack, chunk));
    this.data.push(do_more_fancy_calculations(this.data, chunk));
    this.data.push([ this.data.pop(), 'stuff', this.data.pop() ]);
  }
};

function process() {
  while (work_to_do) {
    stack.do_calculations(get_next_chunk());
  }

  stack.do_calculations(special_chunk);
}

Is there a best practice how to handle this?

Ad

Answer

There's no clear-cut better alternative.

If you want an internal function to pick up a variable from the outside so you don't have to pass it in as a parameter, then yes, by definition the internal function will have to be defined lexically within the outer function in order to have it close over the variable(s) from the outer function.

Personally, I don't see the problem in doing this even if the result is that the function grows longer. I don't feel your function is "cluttered and deeply indented".

Of course, if do_calculations is ever going to be used in any other context outside process, you'll need to define it at the "top" level, in which case you will have to pass in stack.

Your third alternative, using an object, is really equivalent in some sense to passing in stack--except that you're passing it as this (or a property of this) instead of as a parameter. To me, that code looks a bit more contorted.

Your second code fragment seems cleanest to me. By the way, it's great to see someone worrying about this kind of detailed factoring issue.

There is one pattern which may or may not be appropriate in this case. Consider:

function make_calculator(stack) {
  return function(chunk) {
    stack.push(do_some_calculations(stack, chunk));
    stack.push(do_more_fancy_calculations(stack, chunk));
    stack.push([ stack.pop(), 'stuff', stack.pop() ]);
  };
}

function process() {
  var stack = [];

  var do_calculations = make_calculator(stack);

  while (work_to_do) {
    do_calculations(get_next_chunk());
  }

  do_calculations(special_chunk);
}

But this really just passes stack in a slightly different fashion. However, it does have the minor advantage that you have to pass stack only once, to make_calculator, and then the individual calls to do_calculations do not have be cluttered up with the extra stack parameter.

Ad
source: stackoverflow.com
Ad