【问题标题】:How does console.log work?console.log 是如何工作的?
【发布时间】:2011-10-30 00:13:49
【问题描述】:

第一个例子:

在以下示例中:http://jsfiddle.net/maniator/ScTAW/4/
我有这个js:

var storage = (function () {
    var store = [];
    return {
        "add": function (item) {
            store.push(item);
        },
        "get": function () {
            return store;
        }
    };
}());

storage.add('hi there')
console.log(storage, storage.get(), storage.add('hi there #2'));

这是打印到控制台的内容:

对象 [“你好”,“你好 #2”] 未定义

有人会认为控制台应该只说:

对象 [“你好”] 未定义

因为第二次推送不会在值被记录之后发生,因此不应显示。


第二个例子:

在以下示例中:http://jsfiddle.net/maniator/ScTAW/5/

我使用相同的 storage 变量,但我是这样记录的:

storage.add('hi there')
console.log(storage, storage.get(), (function() {
    storage.add('hi there #2');
    console.log('TESTING');
})());

打印到控制台的是:

测试
目的 [“你好”,“你好 #2”] 未定义

嗯嗯,现在很奇怪不是吗?可以期待看到:

对象 [“你好”] 未定义
测试

为什么会这样?控制台日志机制的幕后发生了什么?

【问题讨论】:

  • 我看到了一篇关于此的有趣博文:felix-kling.de/blog/2011/08/18/…
  • 我认为可以肯定地说 log 方法中的所有参数都必须从右到左进行评估,因为左侧的参数可以包含来自右侧参数的数据。
  • @Jamie,那么您选择了错误的示例,因为在那个示例中(就像您的 console.log("The %s jumped over %d tall buildings", "dog", 5) 一样,它们的顺序无关紧要。您似乎认为是因为语义格式取决于参数(或相反?),它们被读取的顺序很重要。但这并不重要,它们都只是 独立的字符串 i> 传递给函数。
  • 评估参数顺序的唯一地方是像i++ + ++i这样的病理情况或OP的例子,你既是reading又是writing 到 内存中的相同位置 相同的指令 中。
  • 在javascript中,函数的参数从左到右求值,然后传递给函数。如果右侧的参数之一修改了左侧涉及的对象,则该函数将获取处于修改状态的对象,因为它是一个引用。哪个参数“需要”哪个其他参数没有任何区别。

标签: javascript console logging


【解决方案1】:

在大多数(如果不是全部)命令式编程语言中,传递给函数调用的任何参数都必须在调用函数之前进行评估(所谓的Eager evaluation)。此外,它们通常按从左到右的顺序进行评估(例如,对于 C,它是未定义的),但是在这两个示例中,评估参数的顺序并不重要。这在详细查看发生的情况时应该很明显:

如前所述,在调用console.log 之前,必须先执行storage.get(),返回store 数组。然后storage.add('hi there #2') 将被执行(或相反),所以它的结果(在这种情况下是undefined,因为add 不返回任何东西)可以作为第三个参数传递给console.log。这意味着一旦console.log 将使用参数(storage, storage.store, undefined) 调用,store 数组已经包含“hi there #2”,因此会产生您观察到的结果。

在第二个例子中,推理还是一样的,只是函数调用有点模糊。乍一看,似乎有一个函数作为第三个参数传递给 console.log 函数;但它实际上是一个函数调用(观察最后的())。所以storage.add('hi there #2')会被执行,然后console.log('TESTING')undefined匿名函数执行的结果会再次传递给console.log

如果您确实将函数传递给console.log,它将打印该函数定义,并且不执行任何操作。所以:

storage.add('hi there')
console.log(storage, storage.get(), (function() {
    storage.add('hi there #2');
    console.log('TESTING');
}));

,最后没有(),结果:

Object
 ["hi there"] function () {
    storage.add('hi there #2');
    console.log('TESTING');
}

我希望这能让事情变得更清楚。

【讨论】:

    【解决方案2】:

    console.log 的所有参数将首先被迭代和评估以组合输出。在迭代您传递的参数时,会对对象进行更改并调用函数。 记录器迭代参数后,输出数据。

    因为对象是 byRef,所以您对 storage.store 对象的“第二个参数”更改会反映在控制台输出中。因为参数是迭代的,所以在组装输出之前调用最后一个参数中的函数调用,因此在看到第一个console.log 调用的输出之前,您会看到函数调用的输出。

    那么值得注意的是,console.log 的输出不会向您显示在调用console.log 时它们存在的对象。对于对象,您实际得到的是对象的引用句柄。因此,在将句柄添加到console.log 的输出之后对对象所做的任何更改仍将反映在对象本身中。由于句柄仅指向对象本身,因此您不会像调用函数时那样获得显示对象状态的输出,而是像现在那样指向对象的实时链接。

    【讨论】:

      【解决方案3】:

      当你像这样打电话给console.log

      console.log(storage, storage.get(), storage.add('hi there #2'));
      

      storage.add('hi there #2') 被评估,返回值被传递给console.log。评估它会导致数组项立即添加到store

      storage.get() 相同 -> store。如此有效,语句变为:

      console.log(storage, store, [return value of add which is undefined]);
      

      当它打印时,store 被评估并且它的内容被输出,这就是你看到["hi there", "hi there #2"]的原因


      在您的第二个示例中,首先评估匿名函数并传递结果。

      【讨论】:

      • 同理,你的内部函数先求值。
      • @Blindy:谢谢。我正准备将其添加到我的答案中。
      • 查看我对@Blindy 回答的评论。
      • @Neal:blindy 的评论说明了一切。你没有通过参考。您正在通过评估结果。
      【解决方案4】:

      您的存储添加函数已完全评估之前 console.log 被调用,因为它是一个参数。

      这并不特定于 console.log,这是每种命令式编程语言的工作原理。

      【讨论】:

      • 当参数被传递时,它通常不是引用,除非你告诉它。因此第二个参数应该保持不变。
      • js中所有对象都是byRef。原语是 byVal。
      • 你的意思是什么?该函数仍然被评估并且返回值仍然被传递给console.log。这里实际上并没有什么神奇的事情发生,如果你有一个将当前时间作为字符串返回的函数,它与console.log(gettime()) 相同。
      • @Blindy -- 是的,但是您是否会希望您的函数像:console.log(gettime(), setTime('+1 days')) 那样易变,即使您想查看当天,它也会将第二天显示为日志? ??
      • 是的,我愿意。你想要什么并不重要,重要的是你清楚表达自己的能力。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-01-19
      • 1970-01-01
      • 2013-11-08
      • 2019-11-09
      • 1970-01-01
      • 2016-11-04
      • 1970-01-01
      相关资源
      最近更新 更多