繁体   English   中英

JavaScript中嵌套函数“类”结构中的变量范围

[英]Variable scope in nested function “Class” structure in Javascript

因此,出于各种原因,我正在将Python应用程序移植到Node.js。 我通过网络开发掌握了一定程度的Javascript知识,但是在可变范围和(可能是)我正在进行的一些异步调用方面遇到了一些问题。

因此,我在DataLoader类中有一个嵌套方法“ xmlToObjectByType”,我试图根据一系列XML文件中的某些匹配条件来设置变量。 无论我在方法中做什么,产品都不会从null更改,并且xml_files.splice()调用永远不会起作用。 我很肯定这是一个范围问题(parseString内的代码位于fs.readFile内,forEach内等),但我还没找到很多运气,无法弄清楚为什么或如何正确获取值集。

作为最后的沟渠,我尝试在xmlToObjectByType上的回调集中获取结果,这使我获得了所需的值,但是我仍然无法从回调中设置products的值。 我敢肯定这与范围界定有关,但我有点茫然。 我敢肯定这是一件非常简单的事情,但是我已经将它深入到JS中已经很长时间了。 根本不是很深。 除了可能令人恐惧的逻辑流程之外,对我在这里做错的事情有任何想法吗?

请注意,这是一个简化版本,出于可读性原因,我检查了其他几种XML文件类型。

var fs = require('fs'),
    xml2js = require('xml2js');


export function DataLoader(working_directory){  
  var working_directory = working_directory;
  var xml_files = [];
  var products = null;
  var data = null;

  var xmlToObjectByType = function(type, setValue) {
    xml_files.forEach(function(file, index) {
        var parser = new xml2js.Parser();
        fs.readFile(working_directory + '/' + file, function(err, data) {
            parser.parseString(data, function (err, result) {
                if (result.Products.Product) {
                    var result_object = result.Products.Product;
                    // check if we've got at least one row, else return false
                    if (result_object.length > 0) {

                        // products specific check
                        if (type == "products") {
                            // identify products XML with artist tag
                            if (result_object[0].Artist) {
                                // this is a products XML file, so pop this file from xml_files, return object
                                xml_files.splice(index, 1);
                                setValue(result_object);
                            } 
                        }

                    } else {
                        // no rows in object
                        setValue("no rows");
                    }
                } else {
                    // ROW object isn't set, malformed XML
                    setValue("malformed XML");
                }
            });
        });
    })
  }

  // check selected directory for XML files
    fs.readdir(working_directory,function(err,files){
        if(err) throw err;
        files.forEach(function(file){
            // do something with each file HERE!
            if (file.split('.').pop() == "xml") {
                xml_files.push(file);
            }
        });

            // if they don't exist return and send message
            if (xml_files.length < 1) {
                var status = {status: "error", message: "There are no XML files in the directory you selected."};
            } else {

                // process further
                xmlToObjectByType("products", function(result) {
                    products = result;
                });


                data = {"products": products};

                // products always has the value null here
                console.log(data);
            }

            return status;
     });
};

我通过

import { DataLoader } from './my_module';
DataLoader('/Path/To/XML');

和XML文件的简化示例(我想我做对了)

<?xml version="1.0" encoding="UTF-8" ?>
<Products>
    <Product>
        <Artist>Test</Artist>
        <Title>Test Title</Title>
        <Description>Maecenas faucibus mollis interdum. Donec sed odio dui. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Aenean lacinia bibendum nulla sed consectetur. Cras mattis consectetur purus sit amet fermentum.</Description>
    </Product>
    <Product>
        <Artist>Test</Artist>
        <Title>Test Title</Title>
        <Description>Maecenas faucibus mollis interdum. Donec sed odio dui. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Aenean lacinia bibendum nulla sed consectetur. Cras mattis consectetur purus sit amet fermentum.</Description>
    </Product>
    <Product>
        <Artist>Test</Artist>
        <Title>Test Title</Title>
        <Description>Maecenas faucibus mollis interdum. Donec sed odio dui. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Aenean lacinia bibendum nulla sed consectetur. Cras mattis consectetur purus sit amet fermentum.</Description>
    </Product>
    <Product>
        <Artist>Test</Artist>
        <Title>Test Title</Title>
        <Description>Maecenas faucibus mollis interdum. Donec sed odio dui. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Aenean lacinia bibendum nulla sed consectetur. Cras mattis consectetur purus sit amet fermentum.</Description>
    </Product>
</Products>

这部分在这里:

xmlToObjectByType("products", function(result) {
    products = result;
});

data = {"products": products};
console.log(data);

我假设的xmlToObjectByType回调是异步的,因此:

data = {"products": products};
console.log(data);

这将在之前运行:

products = result;

这意味着当您为data设置值时,它将是不确定的。 尝试:

xmlToObjectByType("products", function(result) {
    products = result;
    data = {"products": products};
    callback(status, data); // see below
});

DataLoader会,如果你要访问到现在采取的回调data调用后DataLoader

function DataLoader(working_directory, callback)

然后在调用DataLoader需要执行以下操作:

DataLoader(.., function(status, data) {
  // do stuff with status and data
})

Macmee是正确的,因为您的dataproduct变量仅在xmlToObjectByType回调的范围内更改,该回调被异步调用(在您要求将结果记录到控制台后,处理器的下一个滴答)。

但是我认为您的主要问题不是那么多的数据处理,而是您在代码中的哪个点要求获得有关数据的反馈。

因此,与您现有的代码, products 越来越填充表示XML对象,但它只是被填充你问它要打印回给你之后为止。 您可以通过放置一个简单的定时函数来测试该理论,该函数将在半秒内报告结果:

/* ... code before */

var working_directory = working_directory;
var xml_files = [];
var products = null;
var data = null;

setTimeout(function(){

  console.log(products);

}, 500);

/* code after... */

因此,您真正想要的是通常用于DataLoadercallback

function DataLoader(working_directory, callback){ 

并在您的XML解析回调中调用它:

xmlToObjectByType("products", function(result) {
    callback({"products": result});
});

然后像这样调用您的DataLoader

DataLoader('/Path/To/XML', function(data){ console.log(data) });

通过对代码的这些调整,我可以在控制台中获得以下输出:

{ products: 
   [ { Artist: [Object], Title: [Object], Description: [Object] },
     { Artist: [Object], Title: [Object], Description: [Object] },
     { Artist: [Object], Title: [Object], Description: [Object] },
     { Artist: [Object], Title: [Object], Description: [Object] }
   ]
}

显然,我对您的用例做了一些假设。 但是我认为这说明了如何在异步JavaScript中导航不同的作用域。

更新资料

这是您的应用程序的重新编写,它将整理来自多个XML文件的数据,然后使用该数据进行回调:

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


export function DataLoader (directory, callback) {  

  getXmlFiles( function (files) {

    parseXmlFile(files, callback);

  } );

  function parseXmlFile (files, callback) {

    var parser = new xml2js.Parser();
    var data = {};
    var filesLeft = files.length;

    files.forEach( function(file, i) {

      fs.readFile( path.join(directory, file), function (err, result) {

        parser.parseString( result, function (err, result) {

          if (result.Products.Product) {

            var result_object = result.Products.Product;

            if (result_object.length > 0 && result_object[0].Artist) {

              data[file] = result_object;

            }
          }

          filesLeft--;

          if (!filesLeft)
            callback(data);

        });
      });
    });
  }

  function getXmlFiles (callback) {

    var files = [];

    fs.readdir(directory, function (err, f) {

      if(err) throw err;

      f.forEach( function (file) {

        if (file.split('.').pop() == "xml") {

          files.push(file);

        }
      });

      callback(files);

    });
  }
}

将其与以下内容一起使用:

DataLoader( __dirname, function (data) {

  console.log(data);

});

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM