One question regarding function argument

Hello,

I am in middle of this challenge:

And I got one question (or more like a basic concept that needs to be cleared).
This is what I came up with:

const ADD = 'ADD';

const reducer = (state = 0, action) => {
  switch(action.type) {
    case ADD:
      return state + 1;
    default:
      return state;
  }
};

const store = Redux.createStore(reducer);

// Global count variable:
let count = 0;

// Change code below this line
const increment = (count) => ++count;     // passing "count" as an argument 
store.subscribe(increment);
// Change code above this line

store.dispatch({type: ADD});
console.log(count);
store.dispatch({type: ADD});
console.log(count);
store.dispatch({type: ADD});
console.log(count);

And this is the answer:

// Change code below this line
const increment = () => ++count;     // no argument "count" here
store.subscribe(increment);
// Change code above this line

The only difference between mine and the answer is the “count” I tried to pass as the argument.
Intuitively, it makes sense to me that we need “count” as the argument so that the function can do increment on it. However, the result does not support this idea.

To get a better understanding, I also tried codes below to see what’s going on:

let count = 0;
let increment = (count) => ++count;

increment(count);
console.log(count)      // 0

to compare to this case below trying to mimic the issue above:

let count = 0;
let increment = () => ++count;

increment();
console.log(count)        // 1

Can someone tell me what’s the correct concept behind this?
Why passing “count” as the argument doesn’t work?

Thank you.

you need to change the variable outside the function scope

if you give the function a parameter with the same name as the variable outside, the variable outside will never be changed

but why the variable outside can be changed in the other case, where the variable did not get passed to the function through argument?

a function has always access to the outside scope

remember for example this challenge:
Counting Cards

1 Like

As said, the callback is just incrementing a global variable each time an action is dispatched (and as @ieahleen explained it has access to its outer scope). It’s just a contrived example to show how the callback is getting called on each dispatch.

Secondly, your count to the callback is a parameter, not an argument, and nothing is being passed to the callback. So you are in fact trying to increment undefined.

const increment = (count) => {
  console.log(count) // undefined
  ++count;
};
1 Like

Yes, I got it! Thank you!

Thank you for showing console.log(count) inside this “increment” function.
Now I see store.dispatch() does not need “count” as an argument, but this just brought me another question…
We have “count” defined as a global variable:

// Global count variable:
let count = 0;

Shouldn’t console.log(count) at least display “0” as it was defined and set to 0? even it is inside a function (“increment” here)? why would it show undefined?

And I am still stuck in this… why doesn’t this do increment on “count”?

let count = 0;
let increment = (count) => ++count;

increment(count);
console.log(count)      // 0

First off, just to make this absolutely clear, count is not an argument, it is a parameter. They are not the same thing.

const logName = (name) => console.log(name);
logName('John');

name is a parameter, it starts out as undefined (unless given a default value) and is assigned a value when you call the function and pass it some value as the argument.

The string 'John' is an argument, the value passed inside the parentheses to the function when it is called.


Parameters are just function scoped variables that you can pass values to when calling the function. The scope of a parameter is no different from normal function scoped variables. If a function references an identifier (variable name) it will use the identifier in its own scope, or the one closest to it.

const name = 'Jill';

const logName = () => {
  const name = 'John';
  console.log(name);
}

logName();
// 'John'
const name = 'Jill';

const logName = (name) => {
  console.log(name);
}

logName('John');
// 'John'
const name = 'Jill';

const logName = (name) => {
  console.log(name);
}

logName();
// undefined

As you can see from the three code examples, at no point will the name referenced inside the function use the outer scoped variable with the same identifier (name).

That is unless we do not have the same identifier inside the function (either as a parameter or a normal variable).

const name = 'Jill';

const logName = () => {
  console.log(name);
}

logName();
// 'Jill'

So again, the count inside the function is a parameter. It is the identifier closest to the function’s scope. The line ++count is not incrementing the global variable because the function already has an identifier with that name inside its own execution contexts (or Lexical Environment/Variable Environment) so it will use that.


Some resources I highly suggest you go through.



2 Likes

Thank you very much for the explanation and the resources!

I have gone through each resource that you shared and now I am clear.
In this video (@ 19:20) you shared: https://www.youtube.com/watch?v=Nt-qa_LlUH0&feature=emb_logo
At 19:20 of this video, I think he also made a mistake by calling ‘x’ and ‘y’ arguments, which should be parameters, right?

Another question I have is not related to the topic here but by going through this video I also went through the page for hoisting:

I got a bit confused by the last 2 examples in this hoisting page if you don’t mind to answer.

In Example1, it says “no hoisting as there is no var in the statement” but in Example2, it works and it says “initialization also causes declaration”…
and if we move console.log() to the top it also works with hoisting:

console.log(a + "" + b);        // 'Cranberry', this is hoisting as this also display 'Cranberry'?

a = 'Cran'; // Initialize a
b = 'berry'; // Initialize b

isn’t this a conflict against what it claims in Example1 ? since ‘a’ and ‘b’ are not declared with var. Then how can they be hoisted instead of throwing Error?

Thank you.

No, he says “takes in a single argument”, then he highlights the parameter, which is what “takes in” the value passed as an argument. So he is not calling the parameter an argument, he is just showing what the argument is passed to.

I know the terminology around parameters and arguments can get difficult, real fast.

  • A parameter is part of the function/method definition (or signature).

  • An argument is part of the function/method invocation (running, calling it).

  • An argument is always some value.

  • A parameter (which is just a variable) will not contain a value until it is given one, either from an argument passed to the function parameter or if given a default value. Technically in JS the parameter will be given the undefined value, so one might say it always contains a value but that is just a technical detail of how the language works.


The code you posted will throw a ReferenceError. If it did not throw for you, my best guess is you ran the original code first, then change the order, and ran it again.


One more resource You Don’t Know JS Yet: Scope & Closures - 2nd Edition

1 Like

Thank you very much for the explanation.
For hoisting, yes, I think your guess may be right b/c bow I just tried it again with this one and it shows me a different result:

console.log(a + "" + b);        // Error: a is not defined
a = 'Cran'; // Initialize a
b = 'berry'; // Initialize b

But for the original example 2 showed by the MDN page, why does it work? It does not declare by var as mentioned in its Example 1.
Shouldn’t hoisting only happen when it is declared by var and also function declaration?

// Example 2 
// No hoisting, but since initialization also causes declaration (if not already declared), variables are available.

a = 'Cran'; // Initialize a
b = 'berry'; // Initialize b

console.log(a + "" + b); // 'Cranberry'
// Example 1 
// Only y is hoisted

x = 1; // Initialize x, and if not already declared, declare it - but no hoisting as there is no var in the statement.
console.log(x + " " + y); // '1 undefined'
// This prints value of y as undefined as JavaScript only hoists declarations
var y = 2; // Declare and Initialize y

Thank you! I will spend some time to read through this book once I finish Front End Libraries challenges. :slight_smile:

I’m not sure what you mean. Example 2 works because a and b are accessed after they have been assigned a value and the variables are added to the global object (which is what happens when you do not declare the variables).

// in the browser
a = 42;
window.a // 42

I would recommend you read the book I linked to as it explains this subject in great detail and should help answer most of your questions about scope, hoisting, and related subjects.

1 Like

I see, but in Example 1, “y” was declared and also initialized to value “2”, why did it not get hoisting?
Sure, I will definitely read that book you shared! :slight_smile:

y does get hoisted. If it had not been hoisted the code would have thrown a ReferenceError, not print undefined.

Hoisting is part of what is often called the Creation Phase (“compilation” or parsing step), assignment is part of the Execution Phase. The y identifier will be available “ahead of time” (hoisted and will not throw when accessed), the assigned value inside it will not be available “ahead of time”.

You can however run functions before they are declared (if it is a function declaration and not a function expression).

test1('log me'); // 'log me'

function test1(param){
  console.log(param)
}

test2('log me'); // Uncaught TypeError: test2 is not a function

var test2 = function(param){
  console.log(param)
}

Another article
https://blog.bitsrc.io/understanding-execution-context-and-execution-stack-in-javascript-1c9ea8642dd0

A geeky tool

1 Like

Thank you for the explanation. I also found another supporting reason for why “y” is undefined - " Only declarations are hoisted", which matches what you said that its declaration got hoisted but not the intialization.

I feel that there are so much more contents that freecodecamp challenges did not mention at all…
It feels like what we learn from these challenges is probably not even as big as an iceberg of the whole materials that we should learn… :sweat:

Thank you for the link or article and AST explorer, although I am not so clear what AST explorer is about yet :persevere:

Very true, fCC does in no way go this deep. That would have to go into the take-home/extra assignments type challenges. Understanding some of these concepts will definitely help you in debugging and writing code with fewer bugs from the get-go. So good on you for wanting to understand more about some of the deeper technical subjects.

AST = abstract syntax tree, the tool gives you a visual representation of how the parser used will construct the tree from the code you give it. As I said it is geeky and might not be super useful to you. If you look at the article I linked you it might make a bit more sense. It’s just something to play with. AST explorer README for a bit more info.

1 Like

The more I learn the more impossible I feel to land a job by going through fCC challenges/certificates here… as there are too many stuffs fCC does not cover… :sweat:

Thank you. I hope I will still remember to go through this AST topic later. Now there are already too many stuffs ahead of me in the line for me to learn :persevere: