这个声明有什么作用?console.log.bind(控制台)

IT技术 javascript
2021-02-19 12:24:05

我正在使用 JavaScript 并且语句出现问题

console.log.bind(console)

请告诉我这个声明实际上是做什么的。我已经应用了几次,但它没有做任何事情。

4个回答

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

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

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

...但是如果log函数在调用期间依赖于this引用console对象,那么调用f("Message here")将不起作用,因为this不会引用console.

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

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

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

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

那么什么是“主机提供的功能”?规范中未明确定义为JavaScript 的一部分的任何函数,该语言。再说一次,在浏览器上,浏览器相关的功能,比如alertconsole.log等等。

我可以想到该线路可能会给您带来麻烦的两个原因:

  1. 上图:您使用的 JavaScript 引擎无法实现console.log真正的功能。

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

如果最终目标是获得一个您可以调用的函数,例如f("Message here"), for 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,它会调用foothis设置为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;
    }
});
@TJCrowder 你提到了...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.是否有任何可以演示或参考的此类情况(日志依赖于此作为控制台)?
2021-04-22 12:24:05
@Phil 也许我很困惑,但是您是否希望它将这些样式应用于控制台输出?这不是它的工作原理。你所拥有的大致相当于(...args) => console.log('background: #222; color: #bada55', ...args). 也就是说,它只是使用该 css 字符串作为console.log绑定函数的任何调用中的第一个参数我不认为你会能够bind用来实现你想要的。这并不是 console.log 样式的工作原理。更多信息:stackoverflow.com/a/13017382/363701
2021-04-24 12:24:05
@geoidesic:它直接回答了这个问题:var f = console.log.bind(console);理论上,应该给你一个函数,f你可以调用它来登录到控制台。” 它还解释了为什么要这样做,为什么您需要这样做而不仅仅是var f = console.log(这就是this它的来源),以及它如何工作或不工作取决于主机实现。
2021-05-04 12:24:05
@TJCrowder 谢谢你的回答。console.log.bind在 Typescript/Angular 中使用记录器,在控制台中打印内容,并带有指向调用记录器的类的链接,而不是在 Logger 类本身内部,就像没有bind. 不幸的是,我无法传递第二个参数来绑定,例如,console.log.bind(console, 'background: #222; color: #bada55');将简单地将第二个参数打印为字符串而不将其应用于控制台。我该如何解决?
2021-05-04 12:24:05
哇!人们显然喜欢这个答案,但我看不出它是如何回答 OP 问题的?也许我只是愚蠢,但它似乎在敲打实际问题的外围内容(其他答案也是如此)。
2021-05-14 12:24:05

关于这件事的快速更新,您似乎不再需要将控制台绑定到自己了。

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

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

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

TJ Crowder 的回答帮助我解释并解决了我在重定向console.log输出时遇到的问题,但他对“无函数#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用空格分隔参数,所以我也在这里复制了它。对此的主要限制是它不处理作为对象的参数。如果需要,您可以将这些字符串化。

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

通常您可以在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"], ...

这是你对这个问题的看法,它本身就很模糊。重复其他答案没有意义。不过,更改为更通用。
2021-05-06 12:24:05
这似乎根本不是试图回答这个问题。它没有描述什么console.error.bind(console)意思,它只为一个非常特定的用例(问题根本没有提到)提供了一个替代方案(在几个重要的方面表现不同)。
2021-05-11 12:24:05