Reasons to or not to declare functions in an if-statement - javascript

I need some confirmations on what’s happening behind the screen.
There’s an article in MDN that said that we shouldn’t declare functions in a block-level, such as, inside an if-statement. Because it’s inconsistent throughout browsers and anything to do with pre-ES2015 (or pre-ES6).
The function inside the if-statement will not be created unless the condition is true.
I was wondering, IF the condition is true, let’s say 5 minutes later after JavaScript is loaded and set synchronously, will it create the function? Does it still have memory of the code in order to create the function, or is it dumped in unused code and all?
I would like to know whether the function still exists even after the if-statement is completed. Is it accessible? How long is it accessible? Is it accessible until the if-condition is false? Does the result differ from ES6 and pre-ES6? I’ve heard there’s no scope pre-ES6 in if-statements.
e.g.
if (condition) {
function foo() {console.log(“hello world”);
}
}
I was confused after reading an article in MDN on ‘Functions’ under ‘block-level functions in non-strict code’: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions

IF the condition is true, let’s say 5 minutes later after JavaScript is loaded and set synchronously, will it create the function?
The function will be created as soon as the if runs, immediately.
Does it still have memory of the code in order to create the function, or is it dumped in unused code and all?
I would like to know whether the function still exists even after the if-statement is completed. Is it accessible? How long is it accessible?
This behavior will be the same regardless of if the function is declared in an if block or not: if nothing can possibly reference the function in the future (for example, if the block ends and nothing inside the block has a reference to the function), it will eventually be garbage collected. The function may still "exist" in memory for a time until the GC runs.
For example, it should be clear that the following function should always continue to exist, at least until you reload the page:
// IIFE, to get off the top level
(() => {
if (true) {
function foo() {
console.log('clicked');
}
window.addEventListener('click', foo);
}
})();
This is because the addEventListener has been passed a reference to the function.
But the following foo function will get GC'd (maybe a second or few after the page is loaded - it depends on the underlying engine, and isn't visible to Javascript):
// IIFE, to get off the top level
(() => {
if (true) {
function foo() {
console.log('clicked');
}
}
})();
If nothing has saved a reference to the function by the time the block that scopes the variable has finished, the function will not be accessible anywhere, and will be GCd.
The rest of the question looks to be essentially the same as: "where can the function be referenced", which is best described in Bergi's answer here. It's a bit complicated, and the behavior differs depending on whether you're using strict mode, the ES version of the environment, and the environment itself (implementations do not always conform to the specification).
For predictable, easy-to-read code, probably best to simply never use function declarations in non-function blocks; only use function declarations when directly inside function blocks.
(Note that function expressions, where the function is used as a value and passed to something or immediately invoked or explicitly assigned to a variable, are not the same as function declarations - function expressions are fine, it's just the weird behavior of function declarations that's problematic. Also note, per comment, that a "function declaration" is sometimes called "function statement".)

Related

Javascript Hoisting - How are we Accessing Certain Variables?

I have two questions about hoisting:
The way function declarations are hoisted is that they go to the very top, even above variable declarations, to my understanding.
If we have this code:
function fn() {
callback();
}
function callback() {
console.log('why does this show');
}
I don't understand how this works, since both functions are hoisted to the top (which effectively produces the same code that already exists). But callback is still created below fn, and I don't see why we can access it in fn. My guess is it has something to do with the top level objects being able to access each other regardless of lexical position.
Similarly:
var a = 10;
function fn() {
console.log(a);
}
fn();
How does this work? Because the way I understand hoisting makes it seem like the function should be hoisted even above var a, which makes it seem like variables should always be inaccessible in functions.
We can go down the rabbit hole with this but I want to try to give a brief explanation on how your examples work.
Whenever the JavaScript engine creates an execution context (also known as calling stack) whether through functions or code in the global scope it creates an lexical environment. Which is a data structure that holds a collection of name/value pairs about variables and functions in it's own scope or from it's parent scope by using a reference.
About your first example. Both functions get added to the global execution context. If you call fn() in your first example initially. It will then add callback() to the call stack of fn() and execute it accordingly. So, the order of your functions don't really matter in this case.
You're second example is a different case. The execution context knowns you are referring to the global variable and therefor adding a reference to the lexical environment and that makes it able to use the variable inside fn().
This can be quite hard to get a grasp of. There are a ton of resources related to hoisting, scopes, lexical environments and execution contexts so be sure to check those out. :)
This is because how our Javascript Engine parse and compile the code.
I'm not an expert but V8 (The Chorme Engine for JS) in the first file get all the variables and functions names and store all the function references. So, you can "use" a function before "declare" because JS know where the function is.
Some languages, even C++ you could do that and is a nice feature :)

Should closures be used to structure code even if they are not depended on lexical scope? How about redeclaration inside loops?

2 questions:
Closures have the advantage of being able to access the outer scopes and therefore are a great tool in our toolbox.
Is it frowned upon just using them to structure the program if scoping is not needed?.
foo = () => {
closure = (_)=> {
...
}
if(...){
closure(bar);
}else{
closure(baz);
}
}
In this case the function does not depend on the scope and could
be moved one level higher without change in functionality. Semantically it makes sense to place it there since it will only be used inside foo.
How do closures behave if they are declared inside loops? Does redeclaration hurt performance?`
foo.forEach( x => {
closure = () => ...
})
Is it frowned upon just using them to structure the program if scoping is not needed?
There is no one way to write JavaScript code (or any other code). This part of the question calls for opinion, which is off-topic for SO. :-)
There are a couple of objective observations that can be made about doing that:
It keeps the functions private, they can only be used in the function they're created in (assuming you don't return them out of it or assign them to variables declared in an outer scope). That could be argued as being good (for encapsulation) and as bad (limited reuse).
Modules probably reduce the desire to do this a bit (though not entirely).
How do closures behave if they are declared inside loops?
A couple of things I need to call out about your example relative to the question you've asked there:
You haven't declared a function at all. You've created one, via a function expression, but you haven't declared one. (This matters to the answer; we'll come back to it in a moment.)
Your example doesn't create a function in a loop, it creates it inside another function — forEach's callback. That function is called several times, but it isn't a loop per se.
This code creates a function in a loop:
for (const value of something) {
closure = () => {
// ...
};
}
It works just like creating a function anywhere else: A new function object is created each time, closing over the environment where it was created (in this case, the environment for each iteration of the loop). This can be handy if it's using something specific to the loop iteration (like value above).
Declaring a function in a loop looks like this:
for (const value of something) {
function foo() {
// ...
}
}
Never do that in loose-mode code, only do it in strict mode (or better yet, avoid doing it entirely). The loose-mode semantics for it aren't pretty because it wasn't specified behavior for a long time (but was an "allowed extension") and different implementations handled it in different ways. When TC39 specified the behavior, they could only specify a subset of situations that happened to be handled the same way across all major implementations.
The strict mode semantics for it are fairly reasonable: A new function object is created every time and the function's identifier exists only in the environment of the loop iteration. Like all function declarations, it's hoisted (to the top of the block, not the scope enclosing the loop):
"use strict";
const something = [1, 2, 3];
console.log(typeof foo); // Doesn't exist here
for (const value of something) {
foo();
function foo() {
console.log(value);
}
}
console.log(typeof foo); // Doesn't exist here
Does redeclaration hurt performance?
Not really. The JavaScript engine only has to parse the code once, creating the bytecode or machine code for the function, and then can reuse that bytecode or machine code when creating each of the function objects and attaching them to the environment they close over. Modern engines are very good at that. If you're creating millions of temporary objects, that might cause memory churn, but only worry about it if and when you have a performance problem that you've traced to a place where you've done it.

Why is execution context needed for JS

I am new to JS and was learning the role of execution context in JS and asked myself what is the benefit of execution context in JS and why JS is run via execution context. The second question is as we know there are global and functional execution contexts and each context has two phases creation phase and execution phase. So, why we need these two phases? what is the point of having them.
The concept of an "execution context" provides a way of reasoning about what happens when the global environment is created and executed, or when a function is called. It's a conceptual container for the local variables (globals, in the case of the global environment), parameters (for functions), etc. A JavaScript engine can be written without literally having any object it calls an "execution context," as long as it implements the language in keeping with the spec's defined execution context behavior.
One thing that the execution context helps explain is the behavior of closures. A function created within a given execution context has (conceptually) a reference to that context, even after the function call related to the context has completed:
function foo(answer) {
return function say() {
console.log(answer);
};
}
const s = foo(42);
s(); // 42
That works because the function say has a reference to the context of the call to foo that created it (well, more specifically to the thing called its "lexical environment"). That lexical environment continues to exist after foo returns because something still has a reference to it (say). So the call to say afterward works.
The reason for having two phases is to allow use of identifiers before their declaration. This is primarily useful for function declarations:
main();
function main() {
console.log("Worked");
}
The first phase processes the function declarations (and var statements), and then the step-by-step phase runs the code. Without the first phase, the code above would fail as of main(); because main would be undeclared. Of course, with the simple example above, you could just move the main(); call to after the function declaration, but more complicated cases become harder to solve in that way. Languages that didn't have two phases (early C, for instance) had to provide a mechanism for "forward declaring" things that would be defined later. Having two phases means JavaScript doesn't have to have those. (To be fair, C also differs from JavaScript in that it needs to know what all identifiers refer to during compilation, even if the identifiers are in code within functions. So it needed forward declarations just to allow foo and bar to call one another. JavaScript doesn't check the identifiers used within functions until the function is called, so some of the reasons for forward declarations in C wouldn't come up in JavaScript even if it didn't have two phases.)
It wasn't perfectly successful. Having var statements initialize the variables they declare with undefined before the var statement is reached in the code was often the source of bugs and confusion:
console.log(answer); // undefined
var answer = 42;
It's easy for people to be confused by the fact that half of the var answer = 42; was done early (the var answer part), but the other half (answer = 42;) isn't done until later when that statement is reached.
That's why let and const create, but don't initialize, their variables during that first phase. You can use a variable above where it's declared, but only in code that runs after the initialization:
function foo() {
console.log(answer);
}
// foo(); <== Would fail, `answer` isn't initialized yet
let answer = 42;
foo(); // Works, logs 42

Are functions set before variables in the javascript 'creation phase'?

I am doing the Udemy course Javascript: Understanding the Weird Parts right now, and I just learned about the creation phase and the execution phase that occurs when the interpreter interprets the JS.
I have a question, but I will first show you the code I am playing with:
http://codepen.io/rsf/pen/bEgpNY
b();
function b () {
console.log(a);
}
var a = 'peas';
b();
If I understand correctly, in the creation phase, the variables and functions are 'set', meaning they are given spots in memory. The variables are all given the placeholder value of undefined. Then in the execution phase, the engine executes the lines starting at the top. When b() is first called, 'a' still has the placeholder value of undefined, then 'a' is given its initial value of 'peas', b() is called again and this time 'a' has the value of 'peas'.
In my mind, one of two things has to be happening here. Alternative 1: In the creation phase, all variables are set before functions. This means that when a memory space for the function b() is created, the function includes a's value of undefined (because the 'a' memory space was already created with the value of 'undefined'). Alternative 2: the functions and variables are set in the lexical order they are in (in this case, b is created before a), and when b is created, the 'a' reference somehow means that the function is listening for any possible creation of an 'a' memory location, and when later the 'a' location is actually created, the reference refers to that spot.
Am I on the right track with either of these scenarios?
You can think of it like this.
Your original code:
b();
function b () {
console.log(a);
}
var a = 'peas';
b();
is actually executed like this:
var a;
function b () {
console.log(a);
}
b(); // log undefined because a doesn't have a value yet
a = 'peas';
b(); // log peas because a has a value
Basically all the variable and function definitions are hoisted at the top of the enclosing scope.
The order doesn't really matter because the code inside the b function doesn't get executed until you actually call the function.
If I understand correctly, in the creation phase, the variables and functions are 'set', meaning they are given spots in memory.
I would not use the term set for this--it usually is used to refer to a variable being set to (assigned) a particular value. I also would not use the term "spot" or "memory"--we don't need to worry about these internals. It's clearer just to say declared.
I also don't really like the use of the term "creation phase", which is both non-standard and confusing--what is being created, exactly? I would prefer the term "compilation".
The variables are all given the placeholder value of undefined.
To be precise, I would not say they "have the value of undefined", but rather "have no value", or "are not defined". undefined is not a value held by a variable which has not been assigned to yet; rather it's a state, which causes the variable to evaluate to the undefined value when accessed.
Alternative 1: In the creation phase, all variables are set before functions.
Yes, although again it's going to be confusing to use the word "set". Say, "all variables are declared before functions". This is the process of hoisting.
Alternative 2: the functions and variables are set in the lexical order they are in (in this case, b is created before a), and when b is created, the 'a' reference somehow means that the function is listening for any possible creation of an 'a' memory location, and when later the 'a' location is actually created, the reference refers to that spot.
No. The function does not "listen" to anything. It just executes when you tell it to.
Is this important?
Not really. It falls into the category of arcana. So we clog up our brains with rules like, variables hoist this way, function declarations hoist some other way, let has yet some other hoisting behavior. In practice, almost all style guides will call for you to declare variables at the top of the function, and linters will warn you if you don't (or can be configured to do so). This immediately eliminates all variable hoisting issues.
Some people like to put internal functions at the bottom of their function, and that works fine since, if it's a function declaration (ie function foo() { }) the whole thing (including the definition) is hoisted. If it's a function expression being assigned to a variable (ie var foo = function() { }), then it's a variable, and we already decided to put those at the top of our function--see paragraph above.
In general, if your program depends on hoisting behavior, it's written badly. If you need to understand hoisting behavior to understand how the program works, it's written badly.
To summarize, all you really need to learn is one rule: put variable declarations (and their initializations) at the top of your function. Then you don't have to worry about hoisting at all.
(There are some exceptions, such as declaring a variable inside a for statement, as in for (var i...), which is fine, assuming i is not being used for anything other than the index of the loop.)
For some reason, people learning JS seem sometimes to focus on these oddities--such as "why does " " == false or something. I would suggest instead focusing on the how to think about your problems, and break them down, and writing nice clean code that just works, and that you and other people can maintain without worrying about the arcana. I've been writing JS for many years, and cannot remember the last time I encountered a problem related to hoisting.

Function declarations cannot be nested within non-function blocks

I am reading about Function Declarations vs. Function Expressions, and I cannot figure out the meaning of following statement:
Function Declarations occur as standalone constructs and cannot be
nested within non-function blocks.
Someone please to explain with an exemple what does the author means, precisely by: "...cannot be nested within non-function blocks".
Link is: https://javascriptweblog.wordpress.com/2010/07/06/function-declarations-vs-function-expressions/
I dont know the author meant it was physically impossible or more of it shouldn't be done. From my understanding what the author was saying is that this:
var y = true;
if (y) {
function example() {
alert('hi');
return true;
}
}
Here the function is declared inside a conditional statement, which is fine since x is true, but if it were false that function would never be declared and when we do want to the call the example function nothing will happen because it was never declared. So it should be
function example() {
"use strict";
return true;
}
var y = true;
if (y) {
example();
}
In the above code we still call the example function if the condition is met, however since example is defined outside the condition statement we can use it regardless of the conditional statement. This Post has more information about it. Hopefully this is what you meant
Taken at face value, the statement:
Function Declarations occur as standalone constructs and cannot be nested within non-function blocks.
is wrong. It's possible to put function declarations inside blocks, as examples in the article show. The reason that it's warned against is that the behaviour differs in different browsers. In most browsers (not certain versions of IE and Firefox), such functions are declared regardless of whether execution enters the block or not, e.g.:
if (false) {
function foo(){}
}
foo is declared and available within the outer scope. This is exactly the same with variable declarations:
if (false) {
var x = 3;
}
In the above, x is declared regardless of whether the block is executed or not. The assignment of the value, however, only occurs if the block is entered.
Back to functions. The reason function declarations in blocks is warned against is that firstly, it infers that the function is only created if the block is entered, which is incorrect for most browsers but not all. Secondly, and more importantly, it's because different browsers have different behaviour.
Some interesting reading:
Richard Cornford: FunctionExpressions and memory consumption
Kangax: Function statements
What are the precise semantics of block-level functions in ES6?
Also note that function statements are warned against in ES5 strict mode and may be introduced in some future version of ECMAScript.
Finally, this behaviour is addressed directly in ECMA-262 ed 6 in Appendix B 3.3.3 and Appendix B 3.4.
I think it means you cannot define functions arbitrarily in the code, see below.
if true {
function funName(){};
}
funName will not be a function in this case, it will cause an error.
Consider the humble if statement:
function whatever() {
// ...
if (something === somethingElse) {
function aFunction() {
// ...
}
// more code ...
}
aFunction(5, 6, 7);
Now, that code is weird. The function is declared inside the if block. But function declarations are hoisted! So what does that mean?
More weird: what if there's a different declaration for "aFunction" in the else clause?
A fundamental aspect of the weirdness from code like that is that function declarations are treated as if they occur at the top of the scope (that is, they're "hoisted"). For that reason, a function declaration inside some other sort of block is just inherently ambiguous and strange.
Note that function instantiation via function expressions are not weird, because those happen as part of running code, like object initialization expressions.

Categories