Javascript 为什么Node.js在这两种用法中传递参数的方式不同?

Javascript 为什么Node.js在这两种用法中传递参数的方式不同?,javascript,mysql,node.js,Javascript,Mysql,Node.js,查看这个超级简单的node.js程序: var g = { a : 1, b : 2 } function callBack (key, value) { console.log("Callback called with key: " + key + "\nAnd value: " + value) ; } function doNothing (key, value, cb) { true ; console.log(key + ": doing nothing"

查看这个超级简单的node.js程序:

var g = { a : 1, b : 2 }

function callBack (key, value) {
    console.log("Callback called with key: " + key + "\nAnd value: " + value) ;
}

function doNothing (key, value, cb) {
    true ;
    console.log(key + ": doing nothing") ;
    cb() ;
}

function doLoop () {
    for (k in g) {
        f = function () {
            callBack(k, g[k]) ;
        }
        doNothing(k, g[k], f) ;
    }
}

doLoop() ;
运行时,它将生成以下输出:

a: doing nothing
Callback called with key: a
And value: 1
b: doing nothing
Callback called with key: b
And value: 2
嗯。这很有意义——每次调用回调时,它都有正确的参数

现在看看这个节目:

var mysql = require('mysql') ;
var dbClient = undefined ;
var db_uri = "mysql://xxx:xxx@127.0.0.1/xxx" ;

var schema = {
    redirects : "(id int AUTO_INCREMENT, key VARCHAR(50), url VARCHAR(2048))",
    clicks : "(ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP, IP VARBINARY(16))"
} ;

function createOnEmpty(err, results, fields, tableName, create_def) {
    console.log("createOnEmpty called on " + tableName) ;
    if (err) {
        console.error(err) ;
        process.exit(1) ;
    } else {
        if (0 == results.length) {
            dbClient.query(["create table ", tableName, create_def].join(" "),
                           function (err, results, fields) {} ) ;
        } else {
            console.log(tableName + " table already exists.") ;
        }
    }
    console.log("\n\n") ;
}

function setupSchema() {
    for (table in schema) {
        console.log("Checking for table: " + table) ;
        // FIXME: Why does this always seem to pass clicks as tablename?!
        dbClient.query(
           "show tables LIKE '" + table + "'",
           function (err, results, fields) {
               createOnEmpty(err, results, fields, table, schema[table])
           }
        );
    }
}

function handleDBConnect(err) {
    if (err) {
        console.error("ERROR: problem connecting to DB: " + err.code) ;
        process.exit(1) ;
    } else {
        console.log("Connected to database.") ;
        // Automatically set up the schema, if the tables don't exist
        setupSchema() ;
    }
}

function MySQLConnect() {
    dbClient = mysql.createConnection(db_uri) ;
    dbClient.connect(handleDBConnect) ;
}

MySQLConnect() ;
它输出:

Connected to database.
Checking for table: redirects
Checking for table: clicks
createOnEmpty called on clicks



createOnEmpty called on clicks
循环似乎两次都在给参数“clicks”作为参数“table”,即使变量显然已切换为“redirects”


我想我一定对JavaScript/Node在这里的工作方式有一些基本的误解。

调用
dbClient.query
时,循环已经完成,将(隐式全局的)
变量保留在
模式
中的最后一个键处

您需要(参见此处的各种答案)或使用基于回调的迭代,如下所示:

function setupSchema() {
    Object.keys(schema).forEach(function (table) {
        console.log("Checking for table: " + table) ;
        // FIXME: Why does this always seem to pass clicks as tablename?!
        dbClient.query(
           "show tables LIKE '" + table + "'",
           function (err, results, fields) {
               createOnEmpty(err, results, fields, table, schema[table])
           }
        );
    });
}

要了解这种行为,您需要了解以下两个核心js概念:

  • 闭包
  • 事件循环
假设我们有全局变量
a
log
函数将
a
输出到控制台,主函数调用
log
两次,第一次超时(异步),第二次只是简单的函数调用

var a = 42;

function log() {
    console.log(`a is ${a}`);
}

function main() {
    setTimeout(log, 100);
    a = 13;
    log();
}

main();
此代码生成以下输出:

a is 13
a is 13
为什么他妈的第一次a是13

当您调用setTimeout时,它不会阻塞主js线程100ms,它只是将日志函数添加到回调队列中。下一行是
a=13
。由于未在带有
var
关键字的函数体内声明
a
,13分配给在代码第一行声明的
a
。然后,作为
main
函数最后一行的结果,我们有了第一行输出。现在我们有了空的callstack,代码中没有其他事情发生,但回调队列中仍然有log函数。经过100ms后,当且仅当callstack为空(这是我们的情况),
log
函数可以被第二次调用。它再次记录
'a为13'
,因为
a
-s值已被重新分配

这是一个关于异步回调在javascript中如何工作的简短解释,这就是为什么
createOneEmpty在单击时调用了两次
dbClient.query
是异步的,当它第一次被调用时,for循环已经完成了它的执行,
table
的值是
clicks
。 快速而肮脏地解决你的问题将是

for (table in schema) {
    console.log("Checking for table: " + table) ;
    (function (table) {
        dbClient.query("show tables LIKE '" + table + "'",
                   function (err, results, fields) {
                       createOnEmpty(err, results, fields, table,
                                     schema[table]) } );
    )(table);
}
这个立即调用的函数在范围内的每个循环迭代中存储

  • 查看关于eventloop和js中异步工作原理的精彩演讲

伙计,我敢打赌如果你改变这个问题的措辞并添加更多代码来帮助我们理解这个问题。。。你可能会得到你想要的答案。谢谢,这非常有帮助。我知道Javascript作用域是如何工作的,我不明白其中的一些东西!