在IE9中console.log.apply无法工作

47

看起来我重新发明了轮子,但是在Internet Explorer 9中它不起作用,而在IE6中却可以。

function debug()
  if(!window.console) { 
    window.console = { log: function() { /* do something */ } };
  }
  console.log.apply(console, arguments);
}

相关: JavaScript中的Apply()方法问题

F12调试器告诉我这个“对象”(console.log)不支持apply方法。 它甚至不能被识别为一个函数吗? 还有其他指针或想法吗?

7个回答

93

我最近做出的回答的第二部分也回答了这个问题。 我认为这不是那个问题的重复,所以为了方便起见,在这里将其粘贴:

控制台对象不是任何标准的一部分,它是文档对象模型的扩展。像其他DOM对象一样,它被视为主机对象,不需要继承自Object,也不需要像本地ECMAScript函数和对象一样从Function继承其方法。这就是为什么这些方法上的apply和call未定义的原因。在IE 9中,大多数DOM对象都得到了改进,以从本地ECMAScript类型继承。由于开发人员工具被认为是IE的扩展(尽管是内置扩展),它们显然没有像DOM的其余部分一样接受相同的改进。

值得一提的是,您仍然可以使用一些Function.prototype方法在控制台方法上使用一点bind()魔法:

var log = Function.prototype.bind.call(console.log, console);
log.apply(console, ["this", "is", "a", "test"]);
//-> "thisisatest"
所以您可以用同样的方式修复所有console方法,以适应IE 9。
if (Function.prototype.bind && window.console && typeof console.log == "object"){
    [
      "log","info","warn","error","assert","dir","clear","profile","profileEnd"
    ].forEach(function (method) {
        console[method] = this.bind(console[method], console);
    }, Function.prototype.call);
}

这将用调用"host"函数的本地函数替换"host"函数。为了在Internet Explorer 8中使其工作,您需要在代码中包含兼容实现Function.prototype.bindArray.prototype.forEach,或重写以上片段以采用这些方法使用的技术。

另请参见


3
这个解决方案当然引入了一个新的依赖:Function#bind。如果你使用的实现还没有完全符合 ECMAScript5 规范,就必须提供这个依赖。 - T.J. Crowder
1
非常感谢 @Andy,我需要这个来让我的框架在MSIE上的调试器工作。我已经在源代码中加入了鸣谢,再次感谢! - Christian
4
简单实现这个功能的方法是:function debug() { Function.prototype.apply.call(console.log, console, arguments); },这本质上就是这个 bind-ing 代码所做的。 - Sophie Alpert
2
我希望我可以给这个答案点赞超过一次。非常丰富和有用!谢谢! - KOGI
1
@OuuGiii 完成。作为参考,您可以自己编辑任何答案,或者如果您的声望不够高,可以建议由其他用户审核的编辑。 - Andy E
显示剩余8条评论

5

还有一种方法是Paul Irish的做法。它比上面的一些答案更简单,但始终使日志输出为数组(即使只传入一个参数):

// usage: log('inside coolFunc',this,arguments);
// http://paulirish.com/2009/log-a-lightweight-wrapper-for-consolelog/
window.log = function(){
  log.history = log.history || [];   // store logs to an array for reference
  log.history.push(arguments);
  if(this.console){
    console.log( Array.prototype.slice.call(arguments) );
  }
};

谢谢您提出他的好方法。 - line-o

2

IE的一些宿主对象函数并不是真正的JavaScript函数,因此没有applycall。(例如alert。)

所以你必须用较为困难的方式:

function debug()
  var index;

  if(!window.console) { 
    window.console = { log: function() { /* do something */ } };
  }
  for (index = 0; index < arguments.length; ++index) {
      console.log(arguments[index]);
  }
}

没错。在JS中,不是所有可调用的内容都需要是Function对象。 - Tim Down
我一开始也是这么想的。但另一方面,它之前并没有被定义。 - line-o
1
@line-o: 请注意,您在某些地方使用了 window.console,而在另一些地方使用了 console。 现在,所有其他条件相同的情况下,它们应该是等效的,但我们正在谈论的是 IE,如果它为 console.log 播放魔术游戏,我不会感到惊讶。 - T.J. Crowder
@Tim Down: 真的,就像对于RegExp实例的非标准扩展一样,使它们可被调用。 @TJC,@line-o:在您首次启动特定选项卡的开发人员工具之前,控制台(console)对象不存在。 - Andy E

1
我遇到了同样的IE问题,并为此编写了一种例程。 它不像上面所有实现那样花哨,但它在所有现代浏览器中都能工作。
我使用Firefox(Firebug),IE 7,8,9 Chrome和Opera进行了测试。 它利用了邪恶的EVAL,但您只想在开发中进行调试。 之后,您将使用debug = function() {};替换代码。
所以这就是它。
问候,汉斯
(function(ns) {
  var msgs = [];

  // IE compatiblity
  function argtoarr (args,from) {
    var a = [];
    for (var i = from || 0; i<args.length; i++) a.push(args[i]);
    return a;    
  }

  function log(arg) {
    var params = "", format = "", type , output,
        types = {
            "number" : "%d",
            "object" : "{%o}",
            "array" : "[%o]"
        };
    for (var i=0; i<arg.length; i++) {
        params += (params ? "," : "")+"arg["+i+"]";
        type = types[toType(arg[i])] || "%s";
        if (type === "%d" && parseFloat(arg[i]) == parseInt(arg[i], 10)) type = "%f";
        format += (format ? "," : "")+type;
    }
    // opera does not support string format, so leave it out
    output = "console.log("+(window.opera ? "" : "'%f',".replace("%f",format))+"%p);".replace("%p",params);
    eval(output);
  }

  ns.debug = function () {
    msgs.push(argtoarr(arguments));
    if (console !== undefined) while (msgs.length>0) log(msgs.shift());
  }

})(window);

糟糕,我忘记了我的toType函数,这是它。

function toType(obj) {
    if (obj === undefined) return "undefined";
    if (obj === null) return "null";
    var m = obj.constructor;
    if (!m) return "window";
    m = m.toString().match(/(?:function|\[object)\s*([a-z|A-Z|0-9|_|@]*)/);
    return m[1].toLowerCase();
}

0

好的,当你这样写时它可以工作:

function debug()
  if(!window.console) { 
    window.console = {};
    console.log = function() { /* do something */ };
  }
  console.log.apply(console, arguments);
}

行为很奇怪...但如果你这样写'console.log',它会被识别为一个函数。


看到你在我的答案下的评论了,你的做法本应该没问题,但我猜IE浏览器可能会出现一些问题。 - T.J. Crowder

0

尝试:

function log(type) {
  if (typeof console !== 'undefined' && typeof console.log !== 'undefined' &&
    console[type] && Function.prototype.bind) {
    var log = Function.prototype.bind.call(console[type], console);
    log.apply(console, Array.prototype.slice.call(arguments, 1));
  }
}
log('info', 'test', 'pass');
log('error', 'test', 'fail');

适用于logdebuginfowarnerrorgroupgroupEnd


0
我来到这个问题的原因是,我试图为特定模块“调味”console.log函数,通过对参数进行一些玩耍,以便获得更多本地化和深入的调试信息,但IE 9却破坏了它。
@Andy E的答案非常好,并帮助我了解了apply的许多见解。我只是没有采取同样的方法来支持IE9,所以我的解决方案只在“现代浏览器”上运行(现代意味着任何按照我期望的方式运行的浏览器=)
var C = function() {
  var args = Array.prototype.slice.call(arguments);
  var console = window.console;
  args[0]  = "Module X: "+args[0];
  if( typeof console == 'object' && console.log && console.log.apply ){
    console.log.apply(console, args);
  }
};

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接