【发布时间】:2015-02-23 07:27:23
【问题描述】:
我正在使用 JavaScript,但语句出现问题
console.log.bind(console)
请告诉我这句话的实际作用。我已经应用了几次,但它没有做任何事情。
【问题讨论】:
-
IE8 中缺少 Function.bind(),但在真实浏览器中应该可以正常工作。如果有疑问,请发布损坏的代码。
标签: javascript
我正在使用 JavaScript,但语句出现问题
console.log.bind(console)
请告诉我这句话的实际作用。我已经应用了几次,但它没有做任何事情。
【问题讨论】:
标签: javascript
在 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(以及 alert 和 getElementById)不需要是“真正的”JavaScript 函数(尽管在现代浏览器中它们往往是,或者至少非常接近),并且不需要具备所有功能,包括bind。因此,如果您在该行上遇到错误,则可能是您使用该行的引擎不支持 console.log 函数上的 bind。
那么什么是“主机提供的功能”?规范中未明确定义为 JavaScript 的任何函数,该语言。同样,在具有浏览器相关功能的浏览器上,例如 alert 或 console.log 等。
我可以想到这行可能给您带来麻烦的两个原因:
以上内容:您使用的 JavaScript 引擎无法使 console.log 成为真正的函数。
您在 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;
}
});
【讨论】:
var f = console.log.bind(console); 应该给你一个函数,f,你可以调用它来登录控制台。” 它还解释了它为什么这样做,为什么你需要这样做,而不仅仅是var f = console.log(这是this 出现的地方),以及它可能会或可能不会工作,具体取决于主机实现。
console.log.bind 作为记录器,在控制台中打印内容,其中包含指向调用记录器的类的链接,而不是在 Logger 类本身内部,就像没有 bind 一样。不幸的是,我无法将第二个参数传递给绑定,例如console.log.bind(console, 'background: #222; color: #bada55'); 将简单地将第二个参数打印为字符串,而不将其应用于控制台。我该如何解决这个问题?
(...args) => console.log('background: #222; color: #bada55', ...args)。也就是说,它只是在新 bound 函数的任何调用中使用该 css 字符串作为 console.log 的第一个参数。我认为您无法使用bind 来实现您想要的。这不是console.log 样式的工作原理。更多信息:stackoverflow.com/a/13017382/363701
...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.。有没有可以演示或引用的这种情况(日志依赖于此作为控制台)?
关于这件事的快速更新,看来你不需要再绑定控制台了。
Chromium 开始对 console 对象进行一些深入的更改,该对象现在已经绑定到自身。 https://chromium.googlesource.com/chromium/src.git/+/807ec9550e8a31517966636e6a5b506474ab4ea9
似乎所有其他浏览器都遵循了这条路径(在最新版本的 Firefox 和 Node 中进行了测试)。
我猜,如果你需要兼容旧的浏览器,你仍然需要手动绑定控制台,但出于调试目的,你现在可以省略.bind(console) :)
【讨论】:
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 用空格分隔参数,所以我在这里也复制了它。这样做的主要限制是它不处理作为对象的参数。如果需要,您可以对它们进行字符串化。
【讨论】:
正如其他答案中所述,它将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) 的含义,它只是为一个非常具体的用例(问题根本没有提到)提供了一个替代方案(在几个重要方面表现不同)..