Reputation: 17067
function foo(a) {
if (/* Some condition */) {
// perform task 1
// perform task 3
}
else {
// perform task 2
// perform task 3
}
}
I have a function whose structure is similar to the above. I want to abstract task 3 into a function, bar()
, but I wish to limit the access of this function to only within the scope of foo(a)
.
To achieve what I want, is it right to change to the following?
function foo(a) {
function bar() {
// Perform task 3
}
if (/* Some condition */) {
// Perform task 1
bar();
}
else {
// Perform task 2
bar();
}
}
If the above is correct, does bar()
get redefined every time foo(a)
gets called? (I am worrying about waste of CPU resource here.)
Upvotes: 102
Views: 105559
Reputation: 629
I created a jsperf to test nested vs. non-nested and function expressions vs. function declarations, and I was surprised to see that the nested test cases performed 20x faster than non-nested. (I anticipated either the opposite or negligible differences).
https://jsperf.com/nested-functions-vs-not-nested-2/1
This is on Chrome 76, macOS.
Upvotes: 0
Reputation: 1074168
Yes, what you have there is right. Some notes:
bar
is created on every call to the function foo
, but:
bar
does, some engines may determine that they can "inline" it, eliminating the function call entirely. V8 does this, and I'm sure it's not the only engine that does. Naturally they can only do this if it doesn't change the behavior of the code.bar
created every time will vary widely between JavaScript engines. If bar
is trivial, it will vary from undetectable to fairly small. If you're not calling foo
thousands of times in a row (for instance, from a mousemove
handler), I wouldn't worry about it. Even if you are, I'd only worry about it if I saw a problem on slower engines. Here's a test case involving DOM operations, which suggests that there is an impact, but a trivial one (probably washed out by the DOM stuff). Here's a test case doing pure computation which shows a much higher impact, but frankly even, we're talking a difference of microseconds because even a 92% increase on something that takes microseconds to happen is still very, very fast. Until/unless you saw a real-world impact, it's not something to worry about.bar
will only be accessible from within the function, and it has access to all variables and arguments for that call to the function. This makes this a very handy pattern.Upvotes: 146
Reputation: 34800
This is what closures are for.
var foo = (function () {
function bar() {
// perform task 3
};
function innerfoo (a) {
if (/* some cond */ ) {
// perform task 1
bar();
}
else {
// perform task 2
bar();
}
}
return innerfoo;
})();
Innerfoo (a closure) holds a reference to bar and only a reference to innerfoo is returned from an anonymous function which is called only once to create the closure.
Bar is not accessible from the outside this way.
Upvotes: 16
Reputation: 13205
var foo = (function () {
var bar = function () {
// perform task 3
}
return function (a) {
if (/*some condition*/) {
// perform task 1
bar();
}
else {
// perform task 2
bar();
}
};
}());
The closure keeps the scope of bar()
contained, returning the new function from the self-executing anonymous function sets more visible scope to foo()
. The anonymous self-executing function is run exactly once, so there is only one bar()
instance, and every execution of foo()
will use it.
Upvotes: 10
Reputation: 700252
Yes, that works fine.
The inner function is not recreated each time you enter the outer function, but it's re-assigned.
If you test this code:
function test() {
function demo() { alert('1'); }
demo();
demo = function() { alert('2'); };
demo();
}
test();
test();
it will show 1
, 2
, 1
, 2
, not 1
, 2
, 2
, 2
.
Upvotes: 9