澄清学习练习6的说明-使其模块化

时间:2019-09-12 01:25:37

标签: javascript node.js

我很难按照提供的说明进行操作。我不清楚对哪个文件是模块,哪个文件是“原始程序文件”(即“您的程序”等)的引用。

我可以找到此练习的解决方案,并且我模糊地理解它们的解决方案/原因,但是当我尝试从头开始构建时,很难遵循提供的说明。我不清楚对哪个文件是模块,哪个文件是“原始程序文件”的引用。

我的问题是:有人可以为我澄清这些说明吗?其次,我想知道是否可以用一个文件中的所有模块import语句解决此问题-也许是“ main.js”文件? (与在MAKE IT MODULAR模块中导入fs和path相对。)此外,是否可以从模块文件中进行console.log,因此在main.js文件中,您仅需使用参数调用模块函数即可。为了记录过滤后的结果?

我可以看到要求特定于文件的导入的作用域规则,但是有兴趣了解什么是必需的或可能的。

本课从参考上一个练习的解决方案开始,该练习是一个js文件,该文件导入fs和path,并定义一个函数,该函数按指定目录内的文件扩展名对文件进行排序。目录名和文件扩展名字符串作为参数。

MAKE IT MODULAR要求将排序功能放到一个模块中,并使用该模块来提供相同的排序结果,从而扩展先前的答案。

这是我对上一个练习的解决方案。我了解我将不得不移动“。”根据此练习的要求,连接到另一个功能。我还知道我可以使用.forEach方法,如下面的官方解决方案中所使用的。

var fs = require('fs');
var path = require('path');

var fileType = "." + process.argv[3]

fs.readdir(process.argv[2], function (err, list) {
  for (i = 0; i < list.length; i++) {
    if (path.extname(list[i]) === fileType) {
      console.log(list[i]);
    }
  }
});

官方:

var folder = process.argv[2]
var ext = '.' + process.argv[3]

fs.readdir(folder, function (err, files) {
  if (err) return console.error(err)
  files.forEach(function (file) {
    if (path.extname(file) === ext) {
      console.log(file)
    }
  })
})

这是练习:

  

此问题与上一个相同,但引入了模块的概念。您将需要创建两个文件来解决此问题。

     

创建一个程序,该程序在给定目录中打印文件列表,并按文件扩展名过滤。第一个参数是目录名称,第二个参数是扩展过滤器。将文件列表(每行一个文件)打印到控制台。您必须使用异步I / O。

这个“程序”是上一个练习的文件,带有两个参数,对吗?

  

您必须编写模块文件才能完成大部分工作。该模块必须按以下顺序导出一个带有三个参数的函数:目录名,文件名扩展字符串和回调函数。文件扩展名参数必须与传递给程序的参数相同。不要将其转换为RegExp或带“。”前缀。或执行其他任何操作,除非将其传递到模块中,然后在其中执行过滤器所需的操作。

此“模块文件”与上一段(“程序”)中引用的相同,对吗?模块文件导出函数,该文件是导入到main.js之类的文件中吗?

  

文件扩展名参数必须与传递给程序的参数相同。

但是,如果“原始程序”是带有两个参数的函数的文件,那么同一个文件怎么可能是带有三个参数的函数的文件呢?而对“已传递给您的程序”的引用听起来像是“其他”文件就是程序文件?

  

必须使用惯用node(err,data)约定来调用回调函数。该约定规定,除非有错误,否则传递给回调的第一个参数将为null,第二个参数将为您的数据。在本练习中,数据将作为数组过滤后的文件列表。如果您收到错误消息,例如从对fs.readdir()的调用开始,必须以错误(仅错误)作为第一个参数来调用回调。

我的理解是,这两个文件都具有回调函数,所以我认为我必须对两个文件进行错误处理。

  

不得仅从原始程序直接从模块文件直接打印到控制台。

     

万一原始程序文件出现错误,只需对其进行检查并向控制台打印一条信息即可。

好的,现在我真的很困惑……“原始程序”是“模块文件”,不是吗?就像在“模块文件”中那样,是先导出,然后将其导入到诸如main.js或主“程序”文件之类的文件中,而不是导入到“原始”程序中的文件,该程序是在上一个练习中创建的?

最后:

  

这四件事是您的模块必须遵循的契约。

     
      
  1. 导出一个完全采用所述参数的函数。
  2.   
  3. 完全调用一次回调,其中包含错误或所述的某些数据。
  4.   
  5. 请勿更改其他任何内容,例如全局变量或stdout。
  6.   
  7. 处理所有可能发生的错误,并将其传递给回调。
  8.   
     

签订合同的好处是,期望该合同的任何人都可以使用您的模块。因此,您的模块可以由其他那些学习了您的节点或验证者并且可以正常工作的人使用。

我希望“模块”文件是“原始程序”文件,具有导出的带有2个参数的函数,然后将其导入具有3个参数的函数的“主程序”文件,该文件将打印到控制台。

请帮助我使心智模型中的术语与本课措词中指定的术语一致。谢谢。

2 个答案:

答案 0 :(得分:0)

  

这个“程序”是上一个练习的文件,带有两个参数,对吗?

是的,您可以保留上一个练习中的文件,但可以对其进行编辑。它仍然是一个需要两个命令行参数的程序。我们将此文件称为main.js

  

此“模块文件”与上一段(“程序”)中引用的相同,对吗?

不。这是一个新的单独文件。我们称之为filesHelper.js。这个模块将由主模块(“程序”)导入。

新文件将包含上一个练习中的程序确实包含的许多代码,是的。您应将其从main.js移至filesHelper.js,在模块将导出的函数中。

  

模块文件导出函数,该文件是导入到main.js之类的文件中吗?

是的。 filesHelper.js模块文件将声明并导出函数,main.js文件将导入并调用函数。因此,您以前拥有的代码现在将按以下方式分发到文件:

// main.js
… // require the filesHelper

… process.argv[2], process.argv[3] … // a function call with a callback
  … // error handling
  for (… i = 0; i < ….length; i++) { // don't forget to declare the index variable
    console.log(list[i]);
  }
});

// filesHelper.js
var fs = require('fs');
var path = require('path');

… // a function declaration with three parameters
  fs.readdir(process.argv[2], function (err, list) {
    … // error handling (delegate to the callback)
    …
    for (… i = 0; i < list.length; i++) {
      if (path.extname(list[i]) === fileType) {
        … // build the result array
      }
    }
    … // call the callback with the result
  });

… // export the function

填空:-)

  

我想知道是否可以通过一个文件中的所有模块导入语句来解决此问题-也许是main.js文件? (与在fs模块中导入pathfilesHelper.js相对。)

是的,这是有可能的,但是很快就会变得非常笨拙。这就是所谓的“依赖注入”,您可以通过为导出的函数分配5个参数(不仅是3个参数)来实现,这样您就可以将fspath作为额外的参数来传递,而不必无需再将它们导入filesHelper中。

这种方法的缺点是,通常的模块不仅仅具有两个导入,而且必须将数十个导入从另一个模块传递给每个函数,而您调用的另一个模块是无法维护的。另外,您的main.js需要进行所有导入操作,需要了解所编写的每个模块的依赖关系详细信息,并迅速成为God模块。

因此,除非您实际上有一个应用程序用例可以多次使用具有不同依赖性的相同模块,否则请使用常规的声明性模块导入样式。

  

是否可以从模块文件中console.log,以便在main.js文件中只需要使用参数调用模块函数即可记录过滤后的结果?

是的,可以肯定,但是后来它不再是模块化的。除了关注点分离之外,模块化的关键点是可重用性。

假设您有另一个程序main2,该程序想从某个目录中读取具有某个扩展名的所有文件,然后将其内容发送到打印机?如果已对文件名记录进行硬编码,则不能使用filesHelper,但是当您使用带有结果数组调用的通用回调时,可以使用Foo

答案 1 :(得分:0)

经过几天的研究,并使用了上面Bergi的明确答案,我以也许更清楚的方式重新编写了该问题。我仍然认为我无法在learningyounode教程的上下文中回答此问题,因为此练习引入了许多以前的练习中尚未阐明的概念,即回调占位符变量名之间的逻辑划分当调用函数时,将模块中的参数和实际参数中的回调函数定义指定为参数。

  

此问题与上一个相同,但引入了模块的概念。您将需要创建两个文件来解决此问题:main.js和filesHelper.js。

     

本课的高级要求与上一课相同:创建一个程序,该程序打印给定目录中的文件列表,并按文件扩展名过滤。第一个命令行参数是目录名称,第二个参数是扩展过滤器。将文件列表(每行一个文件)打印到控制台。您必须使用异步I / O。

     

您必须编写一个fileHelper.js文件才能完成大部分工作。该文件是模块文件,将包含从您的解决方案到上一练习的许多代码。主要区别在于,模块文件必须按以下顺序导出带有三个参数的单个函数:目录名,文件名扩展字符串和回调函数。文件扩展名参数必须与将通过命令行参数传递给程序的参数相同。不要将其转换为RegExp或带“。”前缀。或执行其他任何操作,除非将其传递到模块中,在此处您可以执行使过滤器功能正常工作所需的操作。

     

要定义单个函数导出,请将函数分配给filesHelper.js中的module.exports对象,从而覆盖已存在的任何值:

module.exports = function (args) { /* ... */ }

  

或可以使用命名函数并分配名称。

function filterFn(args) { /* ... */ }
module.exports = filterFn
  

要在main.js中使用新模块,请以require('fs')相同的方式使用require()调用来加载fs模块。唯一的区别是,对于本地模块,必须以“ ./”为前缀。因此,如果您的文件名为mymodule.js,则:

var mymodule = require('./mymodule.js')

  

.js在这里是可选的,您经常会看到它被省略。现在,您已将模块中的module.exports对象分配给main.js中的mymodule变量。由于您正在导出单个函数,因此mymodule是可以调用的函数!

     

模块的第三个参数(回调函数)在导出的模块函数中将不具有函数定义,而当从主函数调用模块函数时将其定义为实际参数时必须对其进行定义。 js程序文件。 (即,从main.js调用函数时,三个参数将是文件目录,文件类型过滤器和“函数(错误,数据){/ *…* /}”。)但是,有关如何处理错误或数据的说明在导出函数中将需要由回调函数处理。

     

换句话说,“回调”参数/函数的详细信息在主函数和模块函数之间共享。导出函数中的指令负责通过指定如何调用回调来返回错误消息或过滤后的文件列表,在发生错误的情况下,仅使用一个参数(callback(err)),或者使用第一个参数来指定回调的调用方式null,第二个包含过滤后的文件列表作为数组(callback(null,list)),而通过main.js向程序输入实际参数时,将指定实际函数定义。

     

示例:

function bar (callback) {  
  foo(function (err, data) {  
    if (err)  
      return callback(err) // early return

    // ... no error, continue doing cool things with `data`  

    // all went well, call callback with `null` for the error argument  

      callback(null, data)
    })
  }   
  

此外,回调函数在main.js中定义为实际参数时,必须使用惯用的node(err,data)约定来调用,并且在错误冒泡到main.js文件的情况下,只需对其进行检查并向控制台打印一条信息即可。

     

不得仅从main.js直接从模块文件直接打印到控制台。

     

这四件事是您的模块必须遵循的契约。

     
      
  1. 导出一个完全采用所述参数的函数。
  2.   
  3. 仅调用一次回调,但其中包含错误或所述的某些数据。
  4.   
  5. 请勿更改其他任何内容,例如全局变量或stdout。
  6.   
  7. 处理所有可能发生的错误,并将其传递给回调。
  8.   
     

签订合同的好处是,期望该合同的任何人都可以使用您的模块。因此,您的模块可以由其他那些学习了您的节点或验证者并且可以正常工作的人使用。

这是我的通过测试的代码:

//filesHelper.js
var path = require('path')
var fs = require('fs')

module.exports = function (directory, filterString, callbackFunction) {
  fs.readdir(directory, function (err, list) {
    if (err) {
      return callbackFunction(err)
    } else {
      var filteredList = []
      for (let i = 0; i < list.length; i++) {
        if (path.extname(list[i]) === "." + filterString) {
          filteredList.push(list[i])
        }
      }
    }  
    callbackFunction(null, filteredList)
  })
}

...

//main.js
var myModule = require('./filesHelper.js')

var directory = process.argv[2]
var filterString = process.argv[3]

myModule(directory, filterString, function (err, data) {
  if (err) {
    console.log('There was an error: ', err)
  } else {
    for (const file of data) console.log(file)
  }
})