Nathan Boaldin
Nathan Boaldin

Reputation: 315

Understanding nested callbacks and scope?

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);

Upvotes: 1

Views: 636

Answers (2)

Scott Marcus
Scott Marcus

Reputation: 65806

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

Upvotes: 1

Karen Grigoryan
Karen Grigoryan

Reputation: 5422

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.

Upvotes: 1

Related Questions