Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is variable declaration not allowed as a parameter, but function declaration is?

Tags:

javascript

It might be a dumb question. I googled it but can't find the answer. Variable declaration is not allowed as parameter of a function as below.

function t(a) {
    alert(a);
}

t(var x = 1); // Uncaught SyntaxError: Unexpected token var
t(let x = 1); // Uncaught SyntaxError: missing ) after argument list
t(x = 1); // working fine and later I am able to access x also
console.log(x); // printing 1

But function declaration is being allowed as a parameter of a function as below.

function callback(str, f1, f2) {
    if(str == "")
        f1();
    else
        f2();
};

callback("", function b1() { alert("empty") }, function b2() { alert("not empty") }); // working fine

b1(); // Throwing error Uncaught ReferenceError: b1 is not defined

Can anyone please help me to understand

  • Why variable declaration is not allowed as a parameter of a function but function declaration is allowed as parameter?
  • Why we can't access the function which is declared as a function parameter outside of the function call?
like image 813
kadina Avatar asked Jan 04 '19 02:01

kadina


1 Answers

Good question! I'll divide this into parts. There's going to be a lot of material to google here, since your question touches multiple deep subjects.

1. Statements have no value

Declarations are statements. They have no value, and thus they cannot be parameters. That this code...

let a = 1

... has no value, means none of these will work:

doStuff(let a = 1)
let b = (let a = 1)
(let a = 1) + 5

The name a, or a + 5, or f(a) are expressions, and unlike statements, expressions have value. But the declaration of a itself does not.

Note that your intuition about this was not absurd: in other languages, let a = 1 is an expression that evaluates to 1. Not in Javascript.

2. Functions are objects

However, the function keyword does have value: the Function object it defines. Unlike variables, which are language constructs for your convenience, Functions are actual objects that exist in the running program. We say that functions are first-class objects.

You can do all of these:

doStuff(function f() {})
let a = function f() {}
let b = (function f() {}) + 5 // the result of this is funny

Back to your examples, then:

callback(
  "", // a String object
  function b1() { alert("empty") }, // a Function object
  function b2() { alert("not empty") } // a Function object
);

Is similar to this:

function b1() { alert("empty") }
function b2() { alert("not empty") }

callback("", b1, b2)

But not quite. Let's talk about scopes.

3. Names are defined within a scope

The scope of a name, such as a variable or function, is the section(s) of code that have that definition available.

For example:

// Top-level scope:
let a = 1

if (a == 1) {
  // Inner block scope:
  let b = 2
  console.log(a, b) // 1, 2
}

console.log(a, b) // 1, undefined

Scopes live inside larger scopes. Inner scopes can access surrounding scopes, (so a and b are visible inside the block) but not the other way round (so b is not visible outside).

When you created your function objects inside the call...

f(function a() { })

... they were trapped inside an inner scope, and cannot be referenced from outside.

4. Assignments are expressions

In your sample code, you noted that declaring a like this worked:

f(a = 5)

This is... unfortunate. A product of Javascript's history, really. In modern code, you should always use let or const to define variables.

So why does it work? Two reasons. First, because it's an assignment, not a declaration. Like so:

let x = 1
f(x = 2)

Assignments are expressions. They evaluate to the assigned value. The value of x = 2 is 2, and x changes as a side-effect.

5. There is a global scope

The second reason is the unfortunate one. When you avoid the let, var or const keywords, you're implicitly using the global scope.

This is the mother of all scopes, and names that live there are accessible from any point in the code. So, if you just do this...

f(a = 5)

... without having declared a anywhere in the current scope, it's implicitly declared in the global scope, and the assignment takes place. Think of it as this (pseudo-code):

global let a
f(a = 5)

That is, of course, not valid Javascript. But you get the point.

like image 166
slezica Avatar answered Nov 13 '22 08:11

slezica