Separating a Child Function From Its Parent Function Without Using an Object

I am trying to separate a function from its parent function to reduce the size of the parent function but I’m running into some issues. So suppose Function2 is inside of Function1 and there are some variables which are declared in Function1 but are updated inside of Function2. I would like to take Function2 and put it in its own file and then I’m only left with Function2 invocation and a few other things inside of Function1.

If I put Function2 in its own file, the problem I have now is that I can no longer use closure to update the variables inside of Function2 which were declared in Function1. In order to update the variables, I would have to somehow pass the variables to Function2 as arguments and then find a way to return them. Since the variables are value types, I would probably have to redefine them as properties of an object, pass that object to Function2 as an argument, update the properties inside of Function2, and finally return the object. That would not be an issue if I’m dealing with a few child functions and a few variables but I have several of them. Is there an easier way to do this?

Hi @liagapi555,

It would be helpful if we could see some sample code that illustrates your problem?

The following is just a guess at the sort of thing you are trying to achieve, but I’m sure there is more to it.

// function 1
function makeCounter(start = 0) {
  let count = start;

  // function 2
  return function () {
    count++;
    console.log(`count is ${count}`)
  }
}

const counter = makeCounter(5);

counter() // count is 6
counter() // count is 7

Here’s a guide to refactoring, where you extract code out to a separate method.

Yes there is. Do lots and lots of simple easy steps that get you closer to your goal. Even the largest of mountains can be conquered with that simple technique.

Hi All,
Thanks for sharing the guidelines for extracting methods with me but there are things that your examples don’t cover. Please look at my code here. I would like to remove the resize function from the mousedown function and place it in its own file but if I do that many variables which are updated inside of the resize function but are declared outside will need to be passed in as arguments of the resize function. However, since the variables are of value type they cannot be passed in as they are.

That’s just scratching the surface, if I want to put the mousemove function and others like it in their own file there will be even more things to pass as arguments.

I will be investigating this immediately as soon as I get home in about 5 hours from now.
There is a lot that I can get my teeth in to here.

everytime you have many variables, as you said, this is a big indicator for wrong design.

Think about this variables and if it not makes more sense to put them in an object.

For example:

var name = "xxxx“;
var street = “xxxx”;
var city=“xxxxx”;

should be converted into

var person =
{
name: “xxxx”,
street: “xxxx”,
city: “xxxx”
}

In that case you can easily give this object to a function

function changePerson(person)
{
person.name=“yyyy”;
}

console.log(person.name); // will output xxxx
changePerson(person);
console.log(person.name); // will output yyyy

So maybe you can put your variables in an object like this?

1 Like

From the Extract a Method article, steps 1 and 2 have been done where you move some of the code into a function.

There is a good solution for the prevX and prevY at the end of the code. It’s a resize function, so it should only resize. It shouldn’t have anything to do with the event object, for that is mixing in the role of an event handler too.

Let’s separate out the event handler and pass in to the resize function only the information that it needs to know.

First, separating out the event handler:

        // window.addEventListener("mousemove", resize);
        window.addEventListener("mousemove", resizeHandler);
...
        function resizeHandler(e) {
            resize(e);
        }
        function resize(e) {
...
        function mouseup() {
            // window.removeEventListener("mousemove", resize);
            window.removeEventListener("mousemove", resizeHandler);

Then in the handler, we get information that we need from the event object, and pass it to the resize function.

        function resizeHandler(e) {
            const clientX = e.clientX;
            const clientY = e.clientY;
            // resize(e);
            resize(e, clientX, clientY, prevX, prevY);
        }
        // function resize(e) {
        function resize(e, clientX, clientY, prevX, prevY) {
            const rect = el.getBoundingClientRect();

            if (currentResizer.classList.contains("se")) {
                // el.style.width = rect.width - (prevX - e.clientX) + "px";
                el.style.width = rect.width - (prevX - clientX) + "px";
                // el.style.height = rect.height - (prevY - e.clientY) + "px";
                el.style.height = rect.height - (prevY - clientY) + "px";

We can now remove e from the resize function, and in fact we’ll replace it with el instead, and also add currentResizer.

        function resizeHandler(e) {
            const clientX = e.clientX;
            const clientY = e.clientY;
            // resize(e, clientX, clientY, prevX, prevY);
            resize(el, clientX, clientY, prevX, prevY, currentResizer);
        }
        // function resize(e, clientX, clientY, prevX, prevY) {
        function resize(el, clientX, clientY, prevX, prevY, currentResizer) {

Where does that el variable in the handler come from? That’s not a concern for now but we can come to that later.

The resize function also updates prevX and prevY at the end of the function. That’s a bad thing to do because it results in the function doing resizing and updating scoped variables. When the explaination of a function includes an “and”, that usually means that separation is required.

In this case the prevX and prevY statements can be easily moved in to the resizeHandler function.

        function resizeHandler(e) {
            const clientX = e.clientX;
            const clientY = e.clientY;
            resize(el, clientX, clientY, currentResizer);
            prevX = x.clientX;
            prevY = x.clientY;
        }
        function resize(el, clientX, clientY, currentResizer) {
            ...
            // prevX = e.clientX;
            // prevY = e.clientY;
        }

Before the resize function was serving double-purpose as event handler and elment resizer. Now those duties have been separated and teased out, and the resize function can now be easily moved to somewhere else. I’ll move it up above the resizers query selector.

function resize(el, clientX, clientY, currentResizer) {
    ...
}

const resizers = document.querySelectorAll(".resizer");
let currentResizer;

That’s the resizer function taken care of. By making small steps on each occasion, it’s easy to ensure that you don’t break anything, so that the code remains working exactly as it was before.

The updated code is found at https://jsfiddle.net/b85a1Lps/

It has also prompted doing similar things with the rest of the code, where we separate out the event handler function from the function the does the work, deal with closure variables, and make other improvements to the code.

An example of an other improvement to the code, is that the resizer function is seen to only use prevX-clientX, so we can move that calculation out of the function to a variable called deltaX instead.

// function resize(el, clientX, clientY, prevX, prevY, currentResizer) {
function resize(el, deltaX, deltaY, currentResizer) {
...
        el.style.width = rect.width + deltaX + "px";
        el.style.height = rect.height + deltaX + "px";
...
}
            // const clientX = e.clientX;
            // const clientY = e.clientY;
            const deltaX = e.clientX - e.prevX;
            const deltaY = e.clientY - e.prevY;
            // resize(el, clientX, clientY, prevX, prevY, currentResizer);
            resize(el, deltaX, deltaY, currentResizer);

The updated code is at https://jsfiddle.net/b85a1Lps/1/

And, instead of the currentResizer, we can get the resizeDirection and pass that to the resize function.

function getResizeDirection(resizer) {
    const resizeDirections = ["se", "se", "ne", "nw"];
    const direction = resizeDirections.find(function (direction) {
        return resizer.classList.contains(direction);
    });
    return direction;
}

// function resize(el, deltaX, deltaY, currentResizer) {
function resize(el, deltaX, deltaY, direction) {
    const rect = el.getBoundingClientRect();

    // if (currentResizer.classList.contains("se")) {
    if (direction === "se") {
        el.style.width = rect.width + deltaX + "px";
        el.style.height = rect.height + deltaY + "px";
    ...
}
...
        function resizeHandler(e) {
            const deltaX = e.clientX - prevX;
            const deltaY = e.clientY - prevY;
            // resize(el, deltaX, deltaY, currentResizer);
            const direction = getResizeDirection(currentResizer);
            resize(el, deltaX, deltaY, direction);
            prevX = e.clientX;
            prevY = e.clientY;
        }

The updated code is found at https://jsfiddle.net/b85a1Lps/2/

None of this however does anything about changing how the resize behaves. Is that part of the motivation to untangle things in the code?

Hi Paul, Thank you so much for your help. Your code has given me lots of things to consider when refactoring other functions within my app.

1 Like

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.