I want to make my JS code to be less repetitive with an organized look. But I don't know if calling a function from inside another function is a bad practice, like Global Variables.
I share a piece of the code here.
thanks.
function getEx() {
return document.getElementById('example')
}
function getExTwo() {
return document.getElementById("exampleTwo");
}
function getTheValue() {
let getExValue = getEx();
let getExTwoValue = getExTwo();
}
Calling a function from within another function is absolutely not bad coding. That's part of what functions are for, really -- breaking up logical processes into smaller pieces.
Here's an example of how this can work.
// Note: This is new ES6/ES7 syntax for writing JavaScript functions.
// I'm using it here because it's very terse.
const add = (a, b) => a + b;
const multiply = (a, b) => a * b;
const square = (a) => multiply(a, a);
const sumOfSquares = (arr) => {
let sum = 0;
arr.forEach(number => sum += square(number));
return sum;
};
In the (simplified) example above, we use different functions to break up the distinct logical pieces of the problem into smaller, more manageable problems. For example, to calculate the sum of the squares of the array [1, 10, 12], we want to be able to add things and we want to be able to square things, so it's a good idea to create functions for performing each of those steps. We might even want to use other functions within those functions (e.g. calling multiply from within square).
Now, is it possible to go overboard with creating new functions? Yes. Try to avoid writing multiple functions that are basically the same. But otherwise... go nuts!
Calling a function from within another function is not bad. It is a recommended way of reducing repetition by breaking your code into smaller pieces, each handling some specific logic.
Here is a simplified version of your code:
// ps: $ is not from jquery it is just a normal variable.
const $ = document.querySelector
const getValues = () => {
const firstVal = $('#example')
const secondVal = $('#exampleTwo')
}
Related
I am writing a language parser in javascript and went with the functional approach of constructing nested functions to increase scalability.
My code currently looks like the following
const parseDate = (query) => {
return query.match(/stuff/)
}
const parseAuthor = (query) => {
return query.match(/stuff/)
}
const parseTags = (query) => {
return query.match(/stuff/)
}
//...
const transform = (match) => {
const target = match?.[0]?.trim()
// some more post processing that modifies "target"
return target;
}
const parsers = [parseAuthor, parseTags, parseReviewer, parseSearch, parseAfter].map(parser => {
return (query) => transform(parser(query))
})
I am trying to creplace function parseDate in the array with another function that will take the output of parseDate and turn it into ISO format.
parsers[4] = (query) => {
return new Date(parsers[4](query)).toISOString();
};
This causes a RangeError: Maximum call stack size exceeded which I assume is coming from the fact that javascript is constructing a recursive function that takes the output of the old parseDate function and runs it again through the new function, and that output to the same function ... and so on.
That is not what I want. I just want to replace the parsers[4] function with a new one.
I tried duplicating the function but had no luck and getting the same error
parsers[4] = (query) => {
return new Date(parsers[4].bind({})(query)).toISOString();
};
How do we exactly do this?
Since you are adding a function to the list (which is not automatically evaluated while being assigned), your reference to object at the 4th index inside parsers will point to the current state of parsers when executing, which makes it a self reference (leading to an infinite recursion loop causing the stack size to explode).
You could simply use parseDate itself if you have a reference to it or store the current object in parsers[4] in a temporary variable before using it:
var temp = parsers[4]
parsers[4] = (query) => {
return new Date(temp(query)).toISOString();
};
The problem with either form of the definition you're trying to provide is that you're defining a function - parsers[4] - that unconditionally calls itself. Try reading it through and pretending you're the Javascript engine - in order to compute parsers[4](query), you're going to need to first compute parsers[4](query) and so on, infinitely. (The use of .bind doesn't fix that all - yes you're making a new reference to the same function object, but in order to execute that function it still needs to reference the same function you're defining.)
As for how to solve it - there are quite a few ways I can think of. Probably the most simple-minded - but perfectly good enough if you're just doing this once - is to make a temporary copy:
const oldParser = parsers[4];
parsers[4] = (query) => {
return new Date(oldParser(query)).toISOString();
};
You could also write this as a "decorator" or function transformation:
const transformFunction = (func) => (query) => {
return new Date(func(query)).toISOString();
};
parsers[4] = transformFunction(parsers[4]);
Note that the above doesn't lead to infinite recursion - or indeed any recursion at all - even though it may look similar to the original. That's because the original was referring to parsers[4] while executing, while this only refers to it once, when defining the new function. It will simply, under the covers, store a reference to the old function (which I've labelled func), much as the first approach did.
One advantage of this is that, if you needed to transform the whole array in the same way, you could then do it as simply as parsers = parsers.map(transformFunction).
Considering this module in script.js:
const randomizeRange = (min, max) => {
return Math.floor(Math.random() * (max - min) + min);
};
const randomArr = (n, min, max) => {
const arr = [];
for (let i = 0; i < n; i++) {
arr.push(randomizeRange(min, max));
}
return arr;
};
module.exports = { randomArr, randomizeRange };
In the test file below, shouldn't the call count of randomizeRangeMock be 100 because it is being called inside randomArrMock which is called once? Why do I get 0
const randomArr = require("./script").randomArr;
const randomizeRange = require("./script").randomizeRange;
const randomArrMock = jest.fn(randomArr);
const randomizeRangeMock = jest.fn(randomizeRange);
test("tests for randomArr", () => {
expect(randomArrMock(100, 20, 1000)).toHaveLength(100);
expect(randomArrMock.mock.calls.length).toBe(1)
expect(randomizeRangeMock.mock.calls.length).toBe(100) //This fails because its 0
});
I appreciate that you are trying to just understand jest with this question and so I will answer as best I can. I'd normally recommend that you don't mock functions unless you are making some kind of complex back end call to a server or database or whatever. The more real testing you can do with real code (and not mocks) is infinitely more beneficial. That would be my tip for you going forward.
But for the purpose of this question - here's what's going on.
The reason you're not seeing your mocks work in the right way here is because you've essentially created two separate mocks:
You've created a single mock for randomizeArr.
You've created another single, separate mock for randomizeRange.
When you invoke randomArrMock(100, 20, 1000) in your test, this will invoke your single mock for randomizeArr as you've designated but that mock has absolutely no concept of the other mock for randomizeRange ever existing. And so it's never invoked behind the scenes. So this is why you see a 0 for the amount of calls at the end.
It's just a matter of changing your setup a little bit here to designate which mocks are invoked and when in order to see both work together as you're expecting.
What I would actually choose here is not to mock your randomizeRange method at all. That method invoked Math.floor and that's going to be much easier to "spy on" (which is a hint for the method we're going to use here) in this case. That Math.floor method is invoked once with each call to randomizeRange and so it will be an ideal choice. It should also be called 100 times, the same as your method.
The reason we're choosing to spyOn the method rather than to use an actual mock in the way you have done already is because we want to keep the original behaviour and so spying on how many times the method is called without overwriting the intended behaviour is ideal.
So keep your existing mock for randomizeArr as follows:
const randomArrMock = jest.fn(randomArr);
Now set up the spy for Math.floor
const mathFloorSpy = jest.spyOn(Math.prototype, `floor`);
This "spy" will effectively spy on this method without overwriting its behaviour. You can overwrite the behaviour if you want to but we want to keep the behaviour intact while just counting how many times it is called.
So now when you run your test suite:
test("tests for randomArr", () => {
expect(randomArrMock(100, 20, 1000)).toHaveLength(100);
expect(randomArrMock.mock.calls.length).toBe(1)
expect(mathFloorSpy.mock.calls.length).toBe(100)
});
This will now pass as Math.floor will have been invoked each time your randomizeRange method was called.
Finally don't forget to restore the original Math.floor functionality when your test is finished by using:
mathFloorSpy.mockRestore();
Happy testing!
This seems to be a common question, and the most "official" solution I can find was on the Jest GitHub issues page, at https://github.com/facebook/jest/issues/936#issuecomment-545080082.
Since you are using require instead of ES modules transpiled with babel, the solutions described in that linked issue don't seem to apply as well.
I would modify your script.js module to directly reference the exports used, so that the mocks refer to the correct function:
exports.randomizeRange = (min, max) => {
return Math.floor(Math.random() * (max - min) + min);
};
exports.randomArr = (n, min, max) => {
const arr = [];
for (let i = 0; i < n; i++) {
arr.push(exports.randomizeRange(min, max));
}
return arr;
};
And then your test implementation would reference the mocked functions:
const myScript = require('../script');
jest.spyOn(myScript, 'randomArr');
jest.spyOn(myScript, 'randomizeRange');
test("tests for randomArr", () => {
expect(myScript.randomArr(100, 20, 1000)).toHaveLength(100);
expect(myScript.randomArr).toHaveBeenCalledTimes(1);
expect(myScript.randomizeRange).toHaveBeenCalledTimes(100);
});
Using spyOn and toHaveBeenCalledTimes improves readability here.
Now, giving me own piece of advice: Write testable code. If your code is testable, well, you can easily write tests for it, and testable code is generally more modular and flexible; but don't focus on testing implementation details.
In your example, if you want to expose a randomArr method, then don't also expose randomizeRange; your randomArr method can call randomizeRange, but that is an implementation detail you shouldn't worry about.
If you want to make your randomArr much easier to test, consider making the range method (or the range) a parameter. You can then test it without testing some implementation of how it generates random ranges. Something like this:
exports.randomArr = (n, min, max, randomRangeGenerator) => {
const arr = [];
for (let i = 0; i < n; i++) {
arr.push(randomRangeGenerator(min, max));
}
return arr;
};
You can extend this pattern to randomizeRange as well. For testing that method, either pass in the floor and random functions as parameters, or mock them (through the Math object) in your tests. Working with Math.random in tests is difficult since it will produce different values on each run, so it's important mock that.
I have a large data collection that I frequently query by first applying a filter to it. I want to cache and reuse the results of the different filter functions I use because this part can be expensive. Here's a rough simulation of this:
const a = x => x + 1;
const b = x => x + 2;
const m = _.memoize(
f => (console.log('filtering data'), f(314159)),
f => String(f)
);
console.log(m(a));
console.log(m(b));
console.log(m(a));
console.log(m(b));
Here "a" and "b" would be the filter functions I want to use and "m" is acting on the same data each time.
How do I specify the key for the _.memoize function?
The above works but I'm using the string representation of the function which feels wrong. Is there a better way?
I'm worried this isn't safe when minification is applied. In my actual code, the "memoize" part is in one ES6 module, the "a" and "b" parts are in another module and the calls to "m" are in several different modules which import the "a" and "b" function. Will the string representation be stable across modules like this? Is converting to the string representation fast?
The only alternative I can think of is to create a string -> function dictionary so you would do calls like m("a") but JavaScript linters won't pick up if the name is wrong.
The above works but I'm using the string representation of the function which feels wrong.
Indeed.
I'm worried this isn't safe when minification is applied.
No, minification is not the problem. Different functions get minified to different code.
The problem is closures:
const addTo = x => y => x + y
const a = addTo(1),
b = addTo(2);
console.log(String(a) === String(b));
You can only reliably compare functions by their object identity. The best way would probably be to update Lodash to use an ES6 WeakMap that doesn't require any stringification.
As long as such is not available, you can use
const id = Symbol("function identity");
let count = 0;
function toMemoizeKey(fn) {
return fn[id] || (fn[id] = ++count);
}
I'm learning to use Ramda and have come across compose. But I can't grasp the purpose of it
let value = R.compose( calledThird, calledSecond, calledFirst('hello') );
// vs
let value = calledThird( calledSecond( calledFirst('hello') ) );
Is it purely to allow currying? Perhaps...
let curried = R.compose( calledThird, calledSecond, calledFirst );
curried('hello');
Is there any other purpose?
Your second example is exactly the reason for it, although this in fact has nothing to do with currying.
Functional composition allows you to build more sophisticated functions out of simpler ones.
Imagine you have some datatype that you need to sort, let's say a collection of appointments. Given that you already have a sortByDate function and a reverse function, you can write sortByDateDescending as
var sortByDateDescending = function(appointments) {
return reverse(sortByDate(appointments));
}
or in ES6:
const sortByDateDescending = appointments => reverse(sortByDate(appointments));
There is nothing wrong with this. But if you were to write it with a compose helper, it has several advantages:
var sortByDateDescending = compose(reverse, sortByDate);
First of all, it's clearly shorter, and the difference will grow more substantial as you add more functions, especially with pre-es6 code.
But more importantly, this allows you to focus on what's meaningful. You are combining functions here; the data that will eventually be passed through is the goal, but while you're building the new function, it's mostly a distraction.
By writing it this way, you get to focus on what's going on: you are sorting the list by date, and then you are reversing the result.
Languages more closely focused on functional programming make this simpler still, with an unobtrusive operator rather than a function like compose. In Haskell, the equivalent would look like
sortByDateDescending = reverse . sortByDate
But Javascript does not offer that elegance. The best we can do is create functions like compose (or its order-reversed twin, pipe.)
My introduction to Ramda post offers many more examples of working in this style.
Currying, by the way, is a different beast altogether. It is one technique that makes it much easier to reuse functions in such compositions. But it's mostly a distraction here. If you're interested, I also have a post on the issue.
Scott's answer is great - just wanted to add some more real-life examples.
You can identify places to improve code when you see a lot of this sort of pattern, where you're constantly massaging some data with passthrough functions:
var list = [3, 4, 1, 2];
list = filterOutEvens(list);
list = sort(list);
list = prependFoo(list);
You may be tempted to do something like this instead:
[3, 4, 1, 2].filterOutEvens().sort().prependFoo();
But then you remember that to make that possible, you'd have to add stuff to Array.prototype, which is definitely a no-no.
R.compose gives you the next best thing (remember that it works right-to-left):
var processList = R.compose(prependFoo, sort, filterOutEvens);
processList([3, 4, 1, 2]);
Here's the full example if you want to play around with it:
function filterOutEvens(list) {
return list.filter(function(item) {
return item % 2 !== 0;
});
}
function sort(list) {
// More cleanly use R.sort here instead of native Array.prototype.sort, which operates in-place.
var diff = function(a, b) { return a - b; };
return R.sort(diff, list);
}
function prependFoo(list) {
return list.map(function(item) {
return 'foo-' + item;
});
}
var processList = R.compose(prependFoo, sort, filterOutEvens);
var result = processList([3, 4, 1, 2]);
// -> ["foo-1", "foo-3"]
// Display Stackoverflow snippet output.
document.body.innerHTML = JSON.stringify(result, null, 2);
<script src="//cdn.jsdelivr.net/ramda/0.19.1/ramda.min.js"></script>
I have a simple Linked list in my project. The project uses underscore.js. I am trying to think of a way to iterate over the linked list using functional programming techniques. Currently, I have the following:
while(cell.next) {
nxt = cell.next;
//check if next cell fulfills some condition. If condition is met,
// loop breaks
}
Is there any way to implement the above in a functional manner (using underscore.js library)?
Thanks in advance.
Not sure how underscore would factor in. Doesn't really seem necessary to get functional style code.
function doCell(c) {
if (!c || your_condition_is_met)
return c;
return doCell(c.next);
}
var result = doCell(cell);
Not sure what the result is supposed to be, so I just returned the current cell if the condition is met.
You could abstract some of it away into a reusable function if you wish.
function nextWhile(v, callback) {
if (!v || callback(v))
return v;
return nextWhile(v.next, callback);
}
Then use it like this:
var result = nextWhile(cell, function(cell) {
return your_condition_is_met;
});
And this lets you create reusable functions.
function cell_is_something(cell) {
return your_condition_is_met;
}
Which cleans up and documents your code.
var result = nextWhile(cell, cell_is_something);