1

I understand there is a lot of documentation and courses on callbacks. I have read a number of those resources, but for this particular problem I don't understand what is going on. And, I think help with this particular problem will help me understand callbacks better.

In short I do not understand why I can access 'ext' in a part of the main 'readFile()' function, but I can't pass it into functions within that function, ie the 'filterData()' function.

Thanks for your help in understanding this.

const fs = require("fs");
const path = require("path");

const dir1 = process.argv[2];
const fileType = process.argv[3];  
const whenDone = function(err, data) {
  if(err) {
    console.log(err);
  } else {
  for(i = 0; i < data.length - 1; i++) {
    console.log(data[i]);
    }
  }
}

let arr = [];


function readFile(dirName, ext, callback) {
  fs.readdir(dirName, function (err, data) {
    if (err) {
      return callback(err, null);
    }
      // ext and data are defined here:
      console.log(ext, data);
      function filterData(ext) {
        // ext and data are not defined here?
        console.log(ext, data);
        return data.filter(function(files) {
          return arr = files.includes(ext);
          console.log(arr);
        });
        return callback(null, arr);
      }
  });
}




readFile(dir1, fileType, whenDone);
Nathan Boaldin
  • 185
  • 1
  • 4
  • 14
  • 1
    I don't see where `filterData` is called – chiliNUT Mar 03 '18 at 16:24
  • 1
    Probably because you defined ext as an argument to the function which makes it a local variable and overrides any outer scope variables with that name. But... how do you even get there, i do not see a call to `filterData()` or it used as a callback – Patrick Evans Mar 03 '18 at 16:24
  • May be this can help? https://stackoverflow.com/questions/111102/how-do-javascript-closures-work – bp4D Mar 03 '18 at 16:24
  • You're mixing function definition and function call here. Parameter values are defined at the function call, not when a function is defined. – Teemu Mar 03 '18 at 16:40
  • Thank you for this article! – Nathan Boaldin Mar 03 '18 at 16:41

2 Answers2

1

You are defining param name ext within the nested function, which shadows the upper scope ext parameter. So you can change that to something else, and you will have the upper scope ext param access.

Karen Grigoryan
  • 5,234
  • 2
  • 21
  • 35
1

The reason is that in the nested function (filterData), you declare an argument (essentially a local variable) with the same name (ext) that already exists in the higher scope (readFile), so, for the duration of the filterData function, ext refers to what is passed to it, not the ext that was passed to readFile. This is called "hiding" or "shadowing" and happens when a smaller scope declares an identifier that is already declared in a higher scope.

I've written about this here.

If you simply change the name of the nested function's parameters and make sure that within the nested function you refer to those parameter names, it will work:

// The parameters of a function essentially become local variables
// to that function. The names you give will exist throughout that
// function's scope.
function readFile(dirName, ext, callback) {
  fs.readdir(dirName, function (err, data) {
    if (err) {
      return callback(err, null);
    }
    console.log(ext, data);

      // To avoid shadowing/hiding name the nested function's
      // arguments something that doesn't conflict with existing
      // identifier names in the higher scope.
      function filterData(ext2) {
        // Now, there is no collission:
        console.log(ext2, data);
        return data.filter(function(files) {
          return arr = files.includes(ext2);
          console.log(arr);
        });
        return callback(null, arr);
      }
  });
}

Here's a simple example:

var x = 10;
var y = 20;

function foo(){
  var x = true; // <-- This x shadows the one from the higher scope
  console.log(x,y); // true 20, not 10, 20
}

foo();
console.log(x,y);  // 10 20 because the scope of foo is not available here
Scott Marcus
  • 64,069
  • 6
  • 49
  • 71
  • Thank you very much. I'm definitely a Node and callback novice and had no idea about "hiding" or "shadowing". But it makes obvious sense now. – Nathan Boaldin Mar 03 '18 at 16:41
  • @NathanBoaldin JavaScript is "lexically scoped" and, if you ask me, understanding the ins and outs of that concept will unlock a deep understanding of the language. Good luck! – Scott Marcus Mar 03 '18 at 16:43
  • 1
    Thanks a ton for the extra explanation and documentation! Super helpful. – Nathan Boaldin Mar 03 '18 at 16:47