How to understand these specific lines of code

function celebrityName(firstName){
	var nameIntro = "This celebrity is ";

	function lastName(theLastName){
		return nameIntro+firstName+" "+theLastName;
	}
	return lastName;
}

var mjName= celebrityName("Michael");
mjName("Jackson");

I understand that the purpose of this code is just to output: “This celebrity is Michael Jackson” There are a few things that got me confused.

  1. The 1st name “Michael” is being passed through celebrityName() that I can understand. The parameter is being submitted into the function. Like x+y=2, and you submit x=1. But the question is when was time that lastName got submitted into the function?

And let’s say that I have another set of code here:

var nameIntro = "My full name is ";
var firstName = "Kay";
var lastName = "Adam";
function spellName(){
console.log(nameIntro+firstName+lastName);
}
spellName();

The difference between this 1st set code and my code is that he used function lastName to produce the whole sentence while I use the function spellName() to produce the whole sentence. Am I correct?

  1. Why is the last name “Jackson” got subbed into mjName instead of function lastName, it only causes more confusion isn’t it? You’ve already assigned a value to the variable mjName and now you interchange it again? Why?

Could someone explain to me all these? The bottom line is I don’t understand why those code can produce the sentence “The celebrity is michael jackson”. And if I am the one who write this code, I will write it more clearer than the author.

BTW, I found this code from this website that is explaining what is javascript closure.

The function celebrityName literally returns the function lastName.

Now when you set a variable equal to a function, you are setting the variable to what that function returns.

So in this case the variable mjName is being set to what celebrityName returns, which is
the function lastName. This means that the variable mjName is literally a function and has to be used as such.

1 Like

What the author have implemented there is a curry.

(from the reference below)

A curry is a way of constructing functions that allows partial application of a function’s arguments. What this means is that you can pass all of the arguments a function is expecting and get the result, or pass a subset of those arguments and get a function back that’s waiting for the rest of the arguments

Some languages are built around this concept (Haskell for example), it’s a powerful way to abstract your function arguments into ‘sub-arguments’ to keep the program more flexible.

As you stated I will write it more clearer than the author you could have simply wrote a function that accept two parameters and greet that:

function greet(firstN, lastN) {
  return `Hello ${firstN} ${lastN}`;
}

greet('Tony', 'Stark'); // Hello Tony Stark

But what happens if for whatever reason I am not passing the last name directly, but I’m waiting for a query into a database that give it back to me asynchronously… and for a reason that fails.

greet('Bruce') // Hello Bruce undefined

As you can see we specifically told our function to have two parameters, and it will always expect two.
But if we wrote the function as you showed in the example, as a function that wait for another function means we have the flexibility to handle any sub-step:

function greet(firstN) { // return a function
  
  return function(lastN) { // return the actual greet since we reached both firstN and lastN
    return `Hello ${firstN} ${lastN}`;
  }
}

const firstStep = greet('Bruce') // let's start calling the first part of a function.
// we assign its return to a constant so we can call it later.
// firsStep return [Function] --> is waiting for a second call with lastN
... an error occurs on the DB...
... we detected the DB failure, we solved it and now we can resume:
firstStep('Wayne') // Hello Bruce Wayne

Hope it helps :slight_smile:

EDIT: forgot the reference:
currying in functional javascript

1 Like

The scope in Javascript works like this. We have a top-level function (level1()) and two nested functions. Each function has its own local variables. This is sufficiently complicated as to give a pretty good idea of what is going on.

<script>
"use strict"

var glob = "hello";               (TOP)
console.log(window.glob);  // hello

function level1(a){
	let b = "world"
	return level2("what")

	function level2(c){
		let d = 55;
		return level3(7);
		
		function level3(e){
			return function(){
				console.log(glob,a, b,c,d,e)
			} // end anonymous function
		} // end level3
	} // end level2
} // end level1

let foo = level1("what is going on") // sets value of variable a
foo()		       // (A)
/*
hello 
"what is going on"
"world"
"what"
55
7
*/

glob = "changed glob"
foo()		          // (B)
/*
changed glob 
"what is going on"
"world"
"what"
55
7
*/

let anotherFoo = level1("a different function")
anotherFoo()              // (D)
/*
changed glob
"a different function"
"world"
"what"
55
7
*/

</script>

Variables have been defined at the top level (variable* glob* - this becomes part of the global ‘window’ object - see (TOP)),
– at the level of the first function level1() - this function has local variables a,b, and can access glob
– at the level of the first nested function level2() - this function includes its own local variables c,d and can access glob,a,b
– at the level of the second nested function level3() - this function includes its own local variables e,g and can access all the other variables.

Variables a,b,c,d,e, are all local. glob is global. Note that the function arguments define local variables. (These could be pointers - that’s a different subject.)

Now, if level3 did not return a function, all the local variables would disappear - no longer accessible. The variable glob would still be available.

However, because the anonymous function is returned and it references all these variables, the names and their values are packaged up and stored away, and can be accessed whenever the anonymous function is invoked. This is what happens in (A).

Note that the variables a,b,c,d,e are no longer accessible from the global environment - only from the anonymous function.

Note that this packaging also refers to the global variable glob (at least logically - I don’t know about the implementation.). When the value of* glob* is changed, invoking foo() now refers to the new value of glob. Its value in the packaging has changed. This is what happens at (B).

At (D) we create a new function with a different value of a.

You can copy this program and run it yourself. As an exercise, give the anonymous function an argument. As another, try modifying the code to assign e in the anonymous function based on input to level1()

1 Like

Thanks for everyone’s reply! What you guys posted are pure gold for me. @camperextraordinaire @amthomps11

I can get the idea behind all these, but I think I still need some more time to stomach everything you posted above. It kinds of feels like it is out of my scope. (pun intended)
One simple question: Can I just remember this pattern and use it whenever I can cos it is more professional and more accurate? Like just memorize this pattern? I think I understand pretty much the concept of it, and I will carry on with my YDKJS series study hopefully later (not too long) I will bump into similar issues again, and I will give it a more deeper look then!

@sabrawer Thanks for the template! I will look at it whenever I want to revise the concept of scope in js. What you wrote above is very similar with what I read in the books. Thanks!