3
\$\begingroup\$

I come from Java/Swift environment. So, I'm used to show the user of my code only "what is necessary".

For JavaScript I use Visual Code.

When I try to use the Intellisense feature, it shows all my functions/methods when I import it.

So, to hide the unnecessary functions, I opted for using nested functions.

Check out this quicksort code:

/**
 * Sorts the Array in-place
 *
 * @param {Array.<Number | String>} array Array to be sorted
 */
function sort(array) {
    quickSort(array, 0, array.length - 1);

    /**
     * Quicksort implementation
     * 
     * @param {Array.<Number | String>} array 
     * @param {Number} start Start index of the array for quicksort
     * @param {Number} end End index of the array for quicksort
     */
    function quickSort(array, start, end) {
        if (start < end) {
            let pivot = partition(array, start, end);
            quickSort(array, start, pivot - 1);
            quickSort(array, pivot + 1, end);
        }

        /**
         * Partitions the array for Quicksort
         * 
         * @param {Array.<Number | String>} array 
         * @param {Number} left Starting index of starting of array/sub-array
         * @param {Number} right Ending index of starting of array/sub-array
         * @returns {Number} Returns pivot index
         */
        function partition(array, left, right) {
            let pivot = array[right];
            let i = left - 1;
            for (var j = left; j < right; j++) {
                if (array[j] <= pivot) {
                    i++;
                    let temp = array[j];
                    array[j] = array[i];
                    array[i] = temp;
                }
            }
            let temp = array[i + 1];
            array[i + 1] = array[right];
            array[right] = temp;
            return i + 1;
        }
    }

}

Is this nesting appropriate? Because I(beginner) feel like if the nested functions are big, the readability(I mean like figuring out what the code is doing) is an issue.

Or should I just follow the classic _ before the function names to indicate it as a private or not meant to be used by the user?

A simple code breaking example:

For the user of the sort(), partition() and quickSort() is an unnecessary functions. Which in-turn can cause collisions if the user names their function partition() (because I might not have mentioned partition() in my API as it is not usable on its own).

And also any advice to improve the above code is welcome.

\$\endgroup\$
  • \$\begingroup\$ You don't need to nest those functions for them to work: jsfiddle.net/xm4ycq5n. That being said, your question is not exactly clear: what do you mean by unnecessary functions? They seem necessary to me. \$\endgroup\$ – Gerardo Furtado Jul 25 '18 at 2:54
  • \$\begingroup\$ @GerardoFurtado I mean, for the user of the sort(), partition() and quickSort() is an unnecessary functions. Which in-turn can cause collisions if the user names their function partition()(because I might not mentioned partition() in my API as it is not usable on its own) even though the implementation is not related to my sort(). \$\endgroup\$ – SkrewEverything Jul 25 '18 at 2:59
4
\$\begingroup\$

Nesting functions

There is nothing wrong with your approch, a few years back and I would have not said so as some browsers would parse functions within functions each time they were call, with the obvious performance hit. That is a thing of the past and functions are parsed then cached for use next time the function is called.

Modules to keep global scope clean.

Modern JS has modules that let you define exports and then import them as needed in other modules

Each module has its own top level context and thus does not add to the global scope.

Use the export token to define what can be imported.

The only way to access items in the module is via the import token, and only those items explicitly exported.

Modules also use strict mode by default.

Thus you can flatten you code without worrying about name conflicts.

If you create a default export in a module you can also import it under another name.

Example of a module, and flatting your quick sort.

/* in file quickSort.js */
export default function sort(array) {
    quickSort(array, 0, array.length - 1);
}

function quickSort(array, start, end) {
    if (start < end) {
        let pivot = partition(array, start, end);
        quickSort(array, start, pivot - 1);
        quickSort(array, pivot + 1, end);
    }
}

function partition(array, left, right) {
    let pivot = array[right];
    let i = left - 1;
    for (var j = left; j < right; j++) {
        if (array[j] <= pivot) {
            i++;
            let temp = array[j];
            array[j] = array[i];
            array[i] = temp;
        }
    }
    let temp = array[i + 1];
    array[i + 1] = array[right];
    array[right] = temp;
    return i + 1;
}

To access it from another module

import sort from "quickSort";
sort([4,3,6,3,7,8]);

Or use another name, but only for the default export.

import qSort from "quickSort";
qSort([4,3,6,3,7,8]);

Note that modules must be in files for both export code and import code, and the script element must be of type "module" eg <script type="module" src="quickSort.js"></script>

Also as a convention not a requirement modules have the extension .mjs rather than .js and some will use .es.mjs. They all have the same MIME type text/javascript

Singleton

Before modules it was common to use the singleton (AKA immediately invoked function) to reduce or completely eliminate any intrusion to the global scope.

Example of a singleton

const sort = (() => {
    function sort(array) {
        quickSort(array, 0, array.length - 1);
    }

    function quickSort(array, start, end) {
        if (start < end) {
            let pivot = partition(array, start, end);
            quickSort(array, start, pivot - 1);
            quickSort(array, pivot + 1, end);
        }
    }

    function partition(array, left, right) {
        let pivot = array[right];
        let i = left - 1;
        for (var j = left; j < right; j++) {
            if (array[j] <= pivot) {
                i++;
                let temp = array[j];
                array[j] = array[i];
                array[i] = temp;
            }
        }
        let temp = array[i + 1];
        array[i + 1] = array[right];
        array[right] = temp;
        return i + 1;
    }

    return sort;
})();

sort([4,3,6,3,7,8]);
\$\endgroup\$
  • \$\begingroup\$ Singleton seems a nice idea(with out shoving everything inside a function) when we are not using modules. \$\endgroup\$ – SkrewEverything Jul 26 '18 at 18:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.