【问题标题】:What does this statement do? console.log.bind(console)这个声明有什么作用? console.log.bind(控制台)
【发布时间】:2015-02-23 07:27:23
【问题描述】:

我正在使用 JavaScript,但语句出现问题

console.log.bind(console)

请告诉我这句话的实际作用。我已经应用了几次,但它没有做任何事情。

【问题讨论】:

标签: javascript


【解决方案1】:

在 JavaScript 中,函数调用中的this如何函数被调用(对于普通函数,请参见下面的 *)确定。如果它作为检索对象属性的表达式的一部分被调用(例如,foo.bar() 调用 bar() 作为从 foo 获取它的属性检索操作的一部分),则将 this 设置为该属性来自的对象在调用函数期间。

假设您想要更短的console.log,例如f。你可以这样做:

var f = console.log; // <== Suspect!

...但是如果log 函数在调用过程中依赖this 引用console 对象,那么调用f("Message here") 将不起作用,因为this 不会引用@987654335 @。

Function#bind 仅适用于这种情况:它允许您创建一个新函数,当调用该函数时,将调用原始函数并将 this 设置为您提供的值。所以

var f = console.log.bind(console); // Still suspect, for a different reason

...应该,理论上,给你一个函数,f,你可以调用它来登录到控制台。

除了:主机提供的函数,如 console.log(以及 alertgetElementById)不需要是“真正的”JavaScript 函数(尽管在现代浏览器中它们往往是,或者至少非常接近),并且不需要具备所有功能,包括bind。因此,如果您在该行上遇到错误,则可能是您使用该行的引擎不支持 console.log 函数上的 bind

那么什么是“主机提供的功能”?规范中未明确定义为 JavaScript 的任何函数,该语言。同样,在具有浏览器相关功能的浏览器上,例如 alertconsole.log 等。

我可以想到这行可能给您带来麻烦的两个原因:

  1. 以上内容:您使用的 JavaScript 引擎无法使 console.log 成为真正的函数。

  2. 您在 IE 上使用上述行,但开发工具已关闭。在 IE 上,当开发工具未打开时,console 对象未定义,因此该行将抛出 ReferenceError

如果最终目标是获得一个您可以调用的函数,例如 f("Message here"),对于 console.log,那么您可以通过以下方式处理上面的 #1 和 #2:

function f(item) {
    if (typeof console != "undefined" && console.log) {
        console.log(item);
    }
}

那只允许你提供一个项目,而console.log 允许你提供多个项目 (console.log("this", "that", "and the other")),但如果 console.log 可能不是真正的 JavaScript 函数,那么它可能没有 Function#apply,这使得很难包装。

现在,如果您不关心从console.log("this", "that", "and the other") 获得的相同 输出,只要您能看到那里的内容,只需使用console.log(arguments);arguments 是传递给函数的所有参数的内置标识符)。但是如果你想复制准确的输出,你最终会做这样的事情:

function f() {
    var a = arguments;

    if (typeof console != "undefined" && console.log) {
        if (console.log.apply) {
            // It has Function#apply, use it
            console.log.apply(console, arguments);
        } else {
            // Ugh, no Function#apply
            switch (a.length) {
                case 0: console.log(); break;
                case 1: console.log(a[0]); break;
                case 2: console.log(a[0], a[1]); break;
                case 3: console.log(a[0], a[1], a[2]); break;
                case 4: console.log(a[0], a[1], a[2], a[3]); break;
                case 5: console.log(a[0], a[1], a[2], a[3], a[4]); break;
                default:
                    throw "f() only supports up to 5 arguments";
            }
        }
    }
}

...那太丑了。


* ES5 添加了绑定函数,这些函数通过绑定将其this 值附加到它们:

// Normal function
function foo() {
    console.log(this.name);
}

// Create a bound function:
var f = foo.bind(someObject);

无论你如何调用f,它都会调用foo,并将this 设置为someObject

* ES2015(又名 ES6)添加了箭头函数。对于箭头函数,this不是由函数的调用方式设置;相反,该函数从创建它的上下文继承this

// Whatever `this` is here...
var f = () => {                             // <== Creates an arrow function
    // Is what `this` will be here
};

当您在对象方法中执行Array#forEach 之类的操作时,箭头函数非常方便:

this.counter = 0;
this.someArray.forEach(entry => {
    if (entry.has(/* some relevant something */)) {
        ++this.counter;
    }
});

【讨论】:

  • 哇!人们显然喜欢这个答案,但我看不出它是如何回答 OP 问题的?也许我只是很愚蠢,但它似乎在谈论一堆与实际问题无关的东西(其他答案也是如此)。
  • @geoidesic:它直接回答了这个问题:“所以理论上,var f = console.log.bind(console); 应该给你一个函数,f,你可以调用它来登录控制台。” 它还解释了它为什么这样做,为什么你需要这样做,而不仅仅是var f = console.log(这是this 出现的地方),以及它可能会或可能不会工作,具体取决于主机实现。
  • @T.J.Crowder 谢谢你的回答。我在 Typescript/Angular 中使用 console.log.bind 作为记录器,在控制台中打印内容,其中包含指向调用记录器的类的链接,而不是在 Logger 类本身内部,就像没有 bind 一样。不幸的是,我无法将第二个参数传递给绑定,例如console.log.bind(console, 'background: #222; color: #bada55'); 将简单地将第二个参数打印为字符串,而不将其应用于控制台。我该如何解决这个问题?
  • @Phil 也许我很困惑,但您是否希望它将这些样式应用于控制台输出?这不是它的工作原理。你所拥有的大致相当于(...args) =&gt; console.log('background: #222; color: #bada55', ...args)。也就是说,它只是在新 bound 函数的任何调用中使用该 css 字符串作为 console.log 的第一个参数。我认为您无法使用bind 来实现您想要的。这不是console.log 样式的工作原理。更多信息:stackoverflow.com/a/13017382/363701
  • @T.J.Crowder 你提到了...but if the log function relies on this referring to the console object during the call, then calling f("Message here") won't work, because this won't refer to console.。有没有可以演示或引用的这种情况(日志依赖于此作为控制台)?
【解决方案2】:

关于这件事的快速更新,看来你不需要再绑定控制台了。

Chromium 开始对 console 对象进行一些深入的更改,该对象现在已经绑定到自身。 https://chromium.googlesource.com/chromium/src.git/+/807ec9550e8a31517966636e6a5b506474ab4ea9

似乎所有其他浏览器都遵循了这条路径(在最新版本的 Firefox 和 Node 中进行了测试)。

我猜,如果你需要兼容旧的浏览器,你仍然需要手动绑定控制台,但出于调试目的,你现在可以省略.bind(console) :)

【讨论】:

    【解决方案3】:

    T.J. Crowder 的回答帮助我解释并解决了我在重定向 console.log 输出时遇到的问题,但他针对“no Function#apply”案例的解决方案似乎对许多用例造成了任意限制。

    我这样重写了他的代码,这样更简洁、更实用:

    function f() {
        var a = arguments;
    
        if (typeof console != "undefined" && console.log) {
            if (console.log.apply) {
                // It has Function#apply, use it
                console.log.apply(console, arguments);
            } else {
                // Ugh, no Function#apply
                var output = '';
                for (i=0;i<arguments.length;i++) {
                    output += arguments[i] + ' ';
                }
                console.log(output);
            }
        }
    }
    

    console.log 用空格分隔参数,所以我在这里也复制了它。这样做的主要限制是它不处理作为对象的参数。如果需要,您可以对它们进行字符串化。

    【讨论】:

      【解决方案4】:

      正如其他答案中所述,它将console.error 函数作为错误处理程序,bind(console) 使其在其主体中使用console 作为this 的值。否则,this 将被设置为全局对象(浏览器中的window)并且调用将失败。很好解释here

      您通常可以在 Promise 错误处理中看到这一点(例如,来自 Angular 2 快速入门):

      System.import("unmarshaller/Unmarshaller.js").then(null, console.error.bind(console));
      

      题外话:

      您可能希望创建自己的处理程序来预处理错误。在上面的示例中,console.error 在控制台中打印出丑陋的Error,因为SystemJS 只告诉“加载 Unmarshaller.js 时出错”。另一个错误隐藏在originalErr中。

      制作一个自定义处理程序来解包:

      function handleError(e) {
          if (e.originalErr)
              throw e.originalErr;
          throw e;
      }
      
      System.import("unmarshaller/Unmarshaller.js").then(null, handleError);
      

      不需要.bind(),会给你原来抛出的Error,比如:

      错误:给定对象未指定“w:winduptype”且未指定目标类:
      [{"w:winduptype":["FileResource","ArchiveModel:","WarArchiveModel"], ...

      【讨论】:

      • 这似乎根本不是试图回答这个问题。它没有描述console.error.bind(console) 的含义,它只是为一个非常具体的用例(问题根本没有提到)提供了一个替代方案(在几个重要方面表现不同)..
      • 这是你对这个问题的看法,这个问题本身就很模糊。重复其他答案没有意义。不过,改为更通用。
      猜你喜欢
      • 1970-01-01
      • 2016-08-18
      • 2016-05-08
      • 1970-01-01
      • 2012-11-06
      • 2020-07-22
      • 1970-01-01
      • 2015-06-07
      相关资源
      最近更新 更多